Просмотр исходного кода

Merge branch 'master' of http://git.fmode.cn:3000/nkkj/yss-project

Future 5 месяцев назад
Родитель
Сommit
3abef6d8f1
97 измененных файлов с 23763 добавлено и 8465 удалено
  1. 5 2
      src/app/app.routes.ts
  2. 3 0
      src/app/models/project.model.ts
  3. 0 0
      src/app/pages/customer-service/after-sales/after-sales.scss
  4. 241 0
      src/app/pages/customer-service/case-detail/case-detail.component.html
  5. 551 0
      src/app/pages/customer-service/case-detail/case-detail.component.scss
  6. 219 0
      src/app/pages/customer-service/case-detail/case-detail.component.ts
  7. 10 0
      src/app/pages/customer-service/case-library/case-library.html
  8. 43 0
      src/app/pages/customer-service/case-library/case-library.scss
  9. 24 1
      src/app/pages/customer-service/case-library/case-library.ts
  10. 16 0
      src/app/pages/customer-service/consultation-order/consultation-order-dialog.component.html
  11. 111 0
      src/app/pages/customer-service/consultation-order/consultation-order-dialog.component.scss
  12. 40 0
      src/app/pages/customer-service/consultation-order/consultation-order-dialog.component.ts
  13. 81 109
      src/app/pages/customer-service/consultation-order/consultation-order.html
  14. 69 2
      src/app/pages/customer-service/consultation-order/consultation-order.scss
  15. 58 103
      src/app/pages/customer-service/consultation-order/consultation-order.ts
  16. 41 31
      src/app/pages/customer-service/consultation-order/project-group-dialog.component.ts
  17. 25 0
      src/app/pages/customer-service/create-project-modal/create-project-modal.component.html
  18. 6 0
      src/app/pages/customer-service/create-project-modal/create-project-modal.component.scss
  19. 30 0
      src/app/pages/customer-service/create-project-modal/create-project-modal.component.ts
  20. 1 6
      src/app/pages/customer-service/customer-service-layout/customer-service-layout.html
  21. 53 39
      src/app/pages/customer-service/customer-service-layout/customer-service-layout.scss
  22. 27 2
      src/app/pages/customer-service/customer-service-layout/customer-service-layout.ts
  23. 5 0
      src/app/pages/customer-service/customer-service.routes.ts
  24. 14 0
      src/app/pages/customer-service/dashboard/dashboard.html
  25. 5 0
      src/app/pages/customer-service/dashboard/dashboard.ts
  26. 774 0
      src/app/pages/customer-service/dashboard/pages/after-sales/after-sales.component.html
  27. 2012 0
      src/app/pages/customer-service/dashboard/pages/after-sales/after-sales.component.scss
  28. 621 0
      src/app/pages/customer-service/dashboard/pages/after-sales/after-sales.component.ts
  29. 32 3
      src/app/pages/customer-service/dashboard/pages/assignment-list/assignment-list.component.scss
  30. 80 63
      src/app/pages/customer-service/dashboard/pages/consultation-list/consultation-list.component.html
  31. 207 687
      src/app/pages/customer-service/dashboard/pages/consultation-list/consultation-list.component.scss
  32. 94 391
      src/app/pages/customer-service/dashboard/pages/consultation-list/consultation-list.component.ts
  33. 32 3
      src/app/pages/customer-service/dashboard/pages/exception-list/exception-list.component.scss
  34. 0 170
      src/app/pages/customer-service/project-detail/complaint-warning-dialog.ts
  35. 0 162
      src/app/pages/customer-service/project-detail/modification-request-dialog.ts
  36. 0 600
      src/app/pages/customer-service/project-detail/project-detail.html
  37. 0 2706
      src/app/pages/customer-service/project-detail/project-detail.scss
  38. 0 851
      src/app/pages/customer-service/project-detail/project-detail.ts
  39. 0 187
      src/app/pages/customer-service/project-detail/refund-request-dialog.ts
  40. 2 28
      src/app/pages/customer-service/project-list/project-list.html
  41. 1 1
      src/app/pages/customer-service/project-list/project-list.scss
  42. 59 72
      src/app/pages/customer-service/project-list/project-list.ts
  43. 1 1
      src/app/pages/designer/dashboard/dashboard.ts
  44. 295 0
      src/app/pages/designer/project-detail/components/vertical-nav/vertical-nav-styles.scss
  45. 14 0
      src/app/pages/designer/project-detail/components/vertical-nav/vertical-nav.component.html
  46. 148 0
      src/app/pages/designer/project-detail/components/vertical-nav/vertical-nav.component.scss
  47. 56 0
      src/app/pages/designer/project-detail/components/vertical-nav/vertical-nav.component.ts
  48. 310 12
      src/app/pages/designer/project-detail/debug-styles.scss
  49. 841 408
      src/app/pages/designer/project-detail/project-detail.html
  50. 2181 1370
      src/app/pages/designer/project-detail/project-detail.scss
  51. 660 129
      src/app/pages/designer/project-detail/project-detail.ts
  52. 15 7
      src/app/pages/finance/project-records/project-records.html
  53. 85 24
      src/app/pages/finance/project-records/project-records.scss
  54. 18 9
      src/app/pages/finance/reconciliation/reconciliation.scss
  55. 13 8
      src/app/pages/finance/reports/reports.scss
  56. 20 6
      src/app/pages/hr/dashboard/dashboard.scss
  57. 125 1
      src/app/pages/team-leader/dashboard/dashboard.html
  58. 357 0
      src/app/pages/team-leader/dashboard/dashboard.scss
  59. 269 2
      src/app/pages/team-leader/dashboard/dashboard.ts
  60. 3 0
      src/app/services/project.service.ts
  61. 210 0
      src/app/shared/components/complaint-card/complaint-card.html
  62. 530 0
      src/app/shared/components/complaint-card/complaint-card.scss
  63. 224 0
      src/app/shared/components/complaint-card/complaint-card.ts
  64. 249 0
      src/app/shared/components/consultation-order-panel/consultation-order-panel.component.html
  65. 570 0
      src/app/shared/components/consultation-order-panel/consultation-order-panel.component.scss
  66. 209 0
      src/app/shared/components/consultation-order-panel/consultation-order-panel.component.ts
  67. 173 0
      src/app/shared/components/customer-review-card/customer-review-card.html
  68. 425 0
      src/app/shared/components/customer-review-card/customer-review-card.scss
  69. 175 0
      src/app/shared/components/customer-review-card/customer-review-card.ts
  70. 53 39
      src/app/shared/components/designer-nav/designer-nav.scss
  71. 105 0
      src/app/shared/components/order-creation-card/order-creation-card.html
  72. 7 0
      src/app/shared/components/order-creation-card/order-creation-card.scss
  73. 73 0
      src/app/shared/components/order-creation-card/order-creation-card.ts
  74. 420 0
      src/app/shared/components/process-progress-bar/process-progress-bar.component.scss
  75. 189 0
      src/app/shared/components/process-progress-bar/process-progress-bar.ts
  76. 84 0
      src/app/shared/components/process-status-bar/process-status-bar.component.scss
  77. 44 0
      src/app/shared/components/process-status-bar/process-status-bar.ts
  78. 12 0
      src/app/shared/components/progress-bar/progress-bar.component.html
  79. 23 0
      src/app/shared/components/progress-bar/progress-bar.component.ts
  80. 884 0
      src/app/shared/components/proposal-confirm-card/proposal-confirm-card.html
  81. 1676 0
      src/app/shared/components/proposal-confirm-card/proposal-confirm-card.scss
  82. 1252 0
      src/app/shared/components/proposal-confirm-card/proposal-confirm-card.ts
  83. 49 0
      src/app/shared/components/requirements-confirm-card/README.md
  84. 83 0
      src/app/shared/components/requirements-confirm-card/requirements-confirm-card-alternative.html
  85. 256 0
      src/app/shared/components/requirements-confirm-card/requirements-confirm-card-alternative.scss
  86. 765 0
      src/app/shared/components/requirements-confirm-card/requirements-confirm-card.html
  87. 1671 0
      src/app/shared/components/requirements-confirm-card/requirements-confirm-card.scss
  88. 1406 0
      src/app/shared/components/requirements-confirm-card/requirements-confirm-card.ts
  89. 12 0
      src/app/shared/components/requirements-talk-card/requirements-talk-card.html
  90. 11 0
      src/app/shared/components/requirements-talk-card/requirements-talk-card.scss
  91. 13 0
      src/app/shared/components/requirements-talk-card/requirements-talk-card.ts
  92. 111 0
      src/app/shared/components/settlement-card/settlement-card.html
  93. 330 0
      src/app/shared/components/settlement-card/settlement-card.scss
  94. 118 0
      src/app/shared/components/settlement-card/settlement-card.ts
  95. 300 230
      src/app/shared/styles/_hr-dialog.scss
  96. 3 0
      src/styles.scss
  97. 45 0
      src/styles/_variables.scss

+ 5 - 2
src/app/app.routes.ts

@@ -8,8 +8,9 @@ import { CustomerServiceLayout } from './pages/customer-service/customer-service
 import { Dashboard as CustomerServiceDashboard } from './pages/customer-service/dashboard/dashboard';
 import { ConsultationOrder } from './pages/customer-service/consultation-order/consultation-order';
 import { ProjectList } from './pages/customer-service/project-list/project-list';
-import { ProjectDetail } from './pages/customer-service/project-detail/project-detail';
 import { CaseLibrary } from './pages/customer-service/case-library/case-library';
+import { CaseDetailComponent } from './pages/customer-service/case-detail/case-detail.component';
+import { AfterSalesComponent } from './pages/customer-service/dashboard/pages/after-sales/after-sales.component';
 
 // 客服工作台子页面
 import { ConsultationListComponent } from './pages/customer-service/dashboard/pages/consultation-list/consultation-list.component';
@@ -17,6 +18,7 @@ import { AssignmentListComponent } from './pages/customer-service/dashboard/page
 import { ExceptionListComponent } from './pages/customer-service/dashboard/pages/exception-list/exception-list.component';
 
 
+
 // 设计师页面
 import { Dashboard as DesignerDashboard } from './pages/designer/dashboard/dashboard';
 import { ProjectDetail as DesignerProjectDetail } from './pages/designer/project-detail/project-detail';
@@ -68,8 +70,9 @@ export const routes: Routes = [
       { path: 'dashboard', component: CustomerServiceDashboard, title: '客服工作台' },
       { path: 'consultation-order', component: ConsultationOrder, title: '客户咨询与下单' },
       { path: 'project-list', component: ProjectList, title: '项目列表' },
-      { path: 'project-detail/:id', component: DesignerProjectDetail, title: '项目详情' },
       { path: 'case-library', component: CaseLibrary, title: '案例库' },
+      { path: 'case-detail/:id', component: CaseDetailComponent, title: '案例详情' },
+      { path: 'after-sales', component: AfterSalesComponent, title: '售后服务' },
 
       // 工作台子页面路由
       { path: 'consultation-list', component: ConsultationListComponent, title: '咨询列表' },

+ 3 - 0
src/app/models/project.model.ts

@@ -7,6 +7,7 @@ export interface Project {
   highPriorityNeeds: string[];
   status: ProjectStatus;
   currentStage: ProjectStage;
+  stage: ProjectStage; // 添加stage属性,与currentStage保持一致
   createdAt: Date;
   deadline: Date;
   assigneeId: string;
@@ -84,6 +85,7 @@ export interface RenderProgress {
   completionRate: number; // 百分比
   estimatedTimeRemaining: number; // 小时
   status: '进行中' | '已完成' | '已失败';
+  stage?: string; // 添加渲染阶段属性
   updatedAt: Date;
 }
 
@@ -126,6 +128,7 @@ export interface DesignerChange {
   acceptanceTime?: Date;
   historicalAchievements: string[];
   completedWorkload: number; // 百分比
+  reason?: string; // 添加变更原因属性
 }
 
 // 结算记录

+ 0 - 0
src/app/pages/customer-service/after-sales/after-sales.scss


+ 241 - 0
src/app/pages/customer-service/case-detail/case-detail.component.html

@@ -0,0 +1,241 @@
+<!-- 加载状态 -->
+@if (isLoading) {
+  <div class="loading-container">
+    <div class="loading-spinner">
+      <div class="spinner"></div>
+      <p>正在加载案例详情...</p>
+    </div>
+  </div>
+}
+
+<!-- 案例详情内容 -->
+@if (case && !isLoading) {
+  <div class="case-detail-page">
+    <!-- 页面头部 -->
+    <div class="page-header">
+      <div class="header-left">
+        <button class="back-btn" (click)="goBack()">
+          <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor">
+            <path d="m15 18-6-6 6-6"/>
+          </svg>
+          返回案例库
+        </button>
+        <div class="breadcrumb">
+          <span>案例库</span>
+          <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
+            <path d="m9 18 6-6-6-6"/>
+          </svg>
+          <span>{{ case.name }}</span>
+        </div>
+      </div>
+      
+      <div class="header-actions">
+        <button 
+          class="action-btn favorite-btn" 
+          [class.active]="case.isFavorite"
+          (click)="toggleFavorite()"
+        >
+          <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
+            <path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"></path>
+          </svg>
+          {{ case.isFavorite ? '已收藏' : '收藏' }}
+        </button>
+        
+        <button class="action-btn share-btn" (click)="shareCase()">
+          <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor">
+            <circle cx="18" cy="5" r="3"></circle>
+            <circle cx="6" cy="12" r="3"></circle>
+            <circle cx="18" cy="19" r="3"></circle>
+            <line x1="8.59" y1="13.51" x2="15.42" y2="17.49"></line>
+            <line x1="15.41" y1="6.51" x2="8.59" y2="10.49"></line>
+          </svg>
+          分享案例
+        </button>
+      </div>
+    </div>
+
+    <!-- 主要内容区域 -->
+    <div class="main-content">
+      <!-- 左侧:案例图片和基本信息 -->
+      <div class="left-section">
+        <!-- 封面图片 -->
+        <div class="cover-image">
+          <img [src]="case.coverImage" [alt]="case.name" class="cover-img">
+          <div class="image-overlay">
+            <div class="case-badges">
+              <span class="badge project-type">{{ case.projectType }}</span>
+              <span class="badge space-type">{{ case.spaceType }}</span>
+              <span class="badge rendering-level">{{ case.renderingLevel }}</span>
+            </div>
+          </div>
+        </div>
+
+        <!-- 基本信息 -->
+        <div class="basic-info">
+          <h1 class="case-name">{{ case.name }}</h1>
+          
+          <div class="meta-grid">
+            <div class="meta-item">
+              <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor">
+                <path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path>
+                <circle cx="12" cy="7" r="4"></circle>
+              </svg>
+              <div class="meta-content">
+                <span class="meta-label">设计师</span>
+                <span class="meta-value">{{ case.designer }}</span>
+                @if (isInternalUser) {
+                  <span class="team-badge">{{ case.team }}</span>
+                }
+              </div>
+            </div>
+
+            <div class="meta-item">
+              <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor">
+                <rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect>
+                <line x1="3" y1="9" x2="21" y2="9"></line>
+              </svg>
+              <div class="meta-content">
+                <span class="meta-label">面积</span>
+                <span class="meta-value">{{ case.area }}㎡</span>
+              </div>
+            </div>
+
+            <div class="meta-item">
+              <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor">
+                <path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z"></path>
+                <path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z"></path>
+              </svg>
+              <div class="meta-content">
+                <span class="meta-label">浏览量</span>
+                <span class="meta-value">{{ case.viewCount }}</span>
+              </div>
+            </div>
+
+            <div class="meta-item">
+              <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor">
+                <path d="M8 2v4"></path>
+                <path d="M16 2v4"></path>
+                <rect x="3" y="4" width="18" height="18" rx="2" ry="2"></rect>
+                <line x1="3" y1="10" x2="21" y2="10"></line>
+              </svg>
+              <div class="meta-content">
+                <span class="meta-label">创建时间</span>
+                <span class="meta-value">{{ getFormattedDate(case.createdAt) }}</span>
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+
+      <!-- 右侧:详细信息 -->
+      <div class="right-section">
+        <!-- 风格标签 -->
+        <div class="info-section">
+          <h3 class="section-title">设计风格</h3>
+          <div class="style-tags">
+            @for (tag of case.styleTags; track $index) {
+              <span class="style-tag">{{ tag }}</span>
+            }
+          </div>
+        </div>
+
+        <!-- 客户评价 -->
+        @if (case.customerReview) {
+          <div class="info-section">
+            <h3 class="section-title">客户评价</h3>
+            <div class="review-content">
+              <div class="quote-icon">"</div>
+              <p class="review-text">{{ case.customerReview }}</p>
+            </div>
+          </div>
+        }
+
+        <!-- 统计信息 -->
+        <div class="info-section">
+          <h3 class="section-title">案例统计</h3>
+          <div class="stats-grid">
+            <div class="stat-item">
+              <div class="stat-icon">
+                <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor">
+                  <path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z"></path>
+                  <path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z"></path>
+                </svg>
+              </div>
+              <div class="stat-content">
+                <span class="stat-value">{{ case.viewCount }}</span>
+                <span class="stat-label">浏览次数</span>
+              </div>
+            </div>
+
+            <div class="stat-item">
+              <div class="stat-icon">
+                <svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor">
+                  <path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"></path>
+                </svg>
+              </div>
+              <div class="stat-content">
+                <span class="stat-value">{{ case.favoriteCount }}</span>
+                <span class="stat-label">收藏次数</span>
+              </div>
+            </div>
+
+            <div class="stat-item">
+              <div class="stat-icon">
+                <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor">
+                  <circle cx="18" cy="5" r="3"></circle>
+                  <circle cx="6" cy="12" r="3"></circle>
+                  <circle cx="18" cy="19" r="3"></circle>
+                  <line x1="8.59" y1="13.51" x2="15.42" y2="17.49"></line>
+                  <line x1="15.41" y1="6.51" x2="8.59" y2="10.49"></line>
+                </svg>
+              </div>
+              <div class="stat-content">
+                <span class="stat-value">{{ case.shareCount }}</span>
+                <span class="stat-label">分享次数</span>
+              </div>
+            </div>
+          </div>
+        </div>
+
+        <!-- 设计师内部信息 -->
+        @if (isInternalUser) {
+          <div class="info-section internal-section">
+            <h3 class="section-title">内部信息</h3>
+            <div class="internal-grid">
+              <div class="internal-item">
+                <span class="internal-label">案例等级</span>
+                <span class="internal-badge" [class.excellent]="case.isExcellent">
+                  {{ case.isExcellent ? '优秀案例' : '普通案例' }}
+                </span>
+              </div>
+              <div class="internal-item">
+                <span class="internal-label">所属团队</span>
+                <span class="internal-value">{{ case.team }}</span>
+              </div>
+              <div class="internal-item">
+                <span class="internal-label">案例ID</span>
+                <span class="internal-value">{{ case.id }}</span>
+              </div>
+            </div>
+          </div>
+        }
+      </div>
+    </div>
+  </div>
+}
+
+<!-- 案例不存在 -->
+@if (!case && !isLoading) {
+  <div class="error-container">
+    <div class="error-content">
+      <svg width="64" height="64" viewBox="0 0 24 24" fill="none" stroke="currentColor">
+        <circle cx="12" cy="12" r="10"></circle>
+        <line x1="12" y1="8" x2="12" y2="12"></line>
+        <line x1="12" y1="16" x2="12.01" y2="16"></line>
+      </svg>
+      <h3>案例不存在</h3>
+      <p>抱歉,您访问的案例不存在或已被删除</p>
+      <button class="btn btn-primary" (click)="goBack()">返回案例库</button>
+    </div>
+  </div>
+}

+ 551 - 0
src/app/pages/customer-service/case-detail/case-detail.component.scss

@@ -0,0 +1,551 @@
+.case-detail-page {
+  min-height: 100vh;
+  background: #f8f9fa;
+  
+  .page-header {
+    background: white;
+    padding: 20px 24px;
+    border-bottom: 1px solid #e9ecef;
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    position: sticky;
+    top: 0;
+    z-index: 100;
+    
+    .header-left {
+      display: flex;
+      align-items: center;
+      gap: 16px;
+      
+      .back-btn {
+        display: flex;
+        align-items: center;
+        gap: 8px;
+        padding: 8px 16px;
+        background: #f8f9fa;
+        border: 1px solid #dee2e6;
+        border-radius: 6px;
+        color: #495057;
+        cursor: pointer;
+        transition: all 0.2s;
+        
+        &:hover {
+          background: #e9ecef;
+          color: #212529;
+        }
+        
+        svg {
+          stroke-width: 2;
+        }
+      }
+      
+      .breadcrumb {
+        display: flex;
+        align-items: center;
+        gap: 8px;
+        color: #6c757d;
+        font-size: 14px;
+        
+        span:last-child {
+          color: #212529;
+          font-weight: 500;
+        }
+        
+        svg {
+          stroke-width: 2;
+          opacity: 0.5;
+        }
+      }
+    }
+    
+    .header-actions {
+      display: flex;
+      gap: 12px;
+      
+      .action-btn {
+        display: flex;
+        align-items: center;
+        gap: 8px;
+        padding: 10px 16px;
+        border: 1px solid #dee2e6;
+        border-radius: 6px;
+        background: white;
+        color: #495057;
+        cursor: pointer;
+        transition: all 0.2s;
+        font-size: 14px;
+        
+        &:hover {
+          border-color: #007bff;
+          color: #007bff;
+        }
+        
+        &.favorite-btn.active {
+          background: #fff3cd;
+          border-color: #ffc107;
+          color: #856404;
+          
+          svg {
+            fill: #ffc107;
+          }
+        }
+        
+        svg {
+          stroke-width: 2;
+        }
+      }
+    }
+  }
+  
+  .main-content {
+    display: grid;
+    grid-template-columns: 1fr 400px;
+    gap: 24px;
+    padding: 24px;
+    max-width: 1400px;
+    margin: 0 auto;
+    
+    @media (max-width: 1200px) {
+      grid-template-columns: 1fr;
+      gap: 20px;
+    }
+  }
+  
+  .left-section {
+    .cover-image {
+      position: relative;
+      border-radius: 12px;
+      overflow: hidden;
+      background: white;
+      box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+      margin-bottom: 24px;
+      
+      .cover-img {
+        width: 100%;
+        height: 500px;
+        object-fit: cover;
+        display: block;
+      }
+      
+      .image-overlay {
+        position: absolute;
+        top: 0;
+        left: 0;
+        right: 0;
+        bottom: 0;
+        background: linear-gradient(
+          to bottom,
+          rgba(0, 0, 0, 0.3) 0%,
+          transparent 30%,
+          transparent 70%,
+          rgba(0, 0, 0, 0.3) 100%
+        );
+        
+        .case-badges {
+          position: absolute;
+          top: 16px;
+          left: 16px;
+          display: flex;
+          gap: 8px;
+          
+          .badge {
+            padding: 4px 12px;
+            border-radius: 20px;
+            font-size: 12px;
+            font-weight: 500;
+            color: white;
+            
+            &.project-type {
+              background: rgba(0, 123, 255, 0.9);
+            }
+            
+            &.space-type {
+              background: rgba(40, 167, 69, 0.9);
+            }
+            
+            &.rendering-level {
+              background: rgba(255, 193, 7, 0.9);
+              color: #212529;
+            }
+          }
+        }
+      }
+    }
+    
+    .basic-info {
+      background: white;
+      padding: 24px;
+      border-radius: 12px;
+      box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+      
+      .case-name {
+        font-size: 28px;
+        font-weight: 600;
+        color: #212529;
+        margin: 0 0 24px 0;
+        line-height: 1.3;
+      }
+      
+      .meta-grid {
+        display: grid;
+        grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
+        gap: 20px;
+        
+        .meta-item {
+          display: flex;
+          align-items: flex-start;
+          gap: 12px;
+          
+          svg {
+            color: #6c757d;
+            margin-top: 2px;
+            flex-shrink: 0;
+          }
+          
+          .meta-content {
+            flex: 1;
+            
+            .meta-label {
+              display: block;
+              font-size: 12px;
+              color: #6c757d;
+              margin-bottom: 4px;
+              text-transform: uppercase;
+              letter-spacing: 0.5px;
+            }
+            
+            .meta-value {
+              display: block;
+              font-size: 16px;
+              font-weight: 500;
+              color: #212529;
+            }
+            
+            .team-badge {
+              display: inline-block;
+              margin-left: 8px;
+              padding: 2px 8px;
+              background: #e3f2fd;
+              color: #1976d2;
+              border-radius: 12px;
+              font-size: 11px;
+              font-weight: 500;
+            }
+          }
+        }
+      }
+    }
+  }
+  
+  .right-section {
+    display: flex;
+    flex-direction: column;
+    gap: 20px;
+    
+    .info-section {
+      background: white;
+      padding: 20px;
+      border-radius: 12px;
+      box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+      
+      .section-title {
+        font-size: 18px;
+        font-weight: 600;
+        color: #212529;
+        margin: 0 0 16px 0;
+        display: flex;
+        align-items: center;
+        gap: 8px;
+      }
+      
+      &.internal-section {
+        border: 2px solid #e3f2fd;
+        background: #fafbfc;
+        
+        .section-title {
+          color: #1976d2;
+        }
+      }
+    }
+    
+    .style-tags {
+      display: flex;
+      flex-wrap: wrap;
+      gap: 8px;
+      
+      .style-tag {
+        padding: 6px 12px;
+        background: #f8f9fa;
+        border: 1px solid #dee2e6;
+        border-radius: 20px;
+        font-size: 13px;
+        color: #495057;
+        transition: all 0.2s;
+        
+        &:hover {
+          background: #e9ecef;
+          border-color: #adb5bd;
+        }
+      }
+    }
+    
+    .review-content {
+      position: relative;
+      padding: 16px;
+      background: #f8f9fa;
+      border-radius: 8px;
+      border-left: 4px solid #007bff;
+      
+      .quote-icon {
+        position: absolute;
+        top: -8px;
+        left: 12px;
+        font-size: 32px;
+        color: #007bff;
+        background: white;
+        padding: 0 4px;
+      }
+      
+      .review-text {
+        margin: 0;
+        font-style: italic;
+        color: #495057;
+        line-height: 1.6;
+      }
+    }
+    
+    .stats-grid {
+      display: grid;
+      grid-template-columns: 1fr;
+      gap: 16px;
+      
+      .stat-item {
+        display: flex;
+        align-items: center;
+        gap: 12px;
+        padding: 12px;
+        background: #f8f9fa;
+        border-radius: 8px;
+        
+        .stat-icon {
+          width: 40px;
+          height: 40px;
+          background: #007bff;
+          border-radius: 8px;
+          display: flex;
+          align-items: center;
+          justify-content: center;
+          color: white;
+          flex-shrink: 0;
+          
+          svg {
+            stroke-width: 2;
+          }
+        }
+        
+        .stat-content {
+          flex: 1;
+          
+          .stat-value {
+            display: block;
+            font-size: 20px;
+            font-weight: 600;
+            color: #212529;
+            line-height: 1;
+          }
+          
+          .stat-label {
+            display: block;
+            font-size: 12px;
+            color: #6c757d;
+            margin-top: 2px;
+          }
+        }
+      }
+    }
+    
+    .internal-grid {
+      display: flex;
+      flex-direction: column;
+      gap: 12px;
+      
+      .internal-item {
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+        padding: 8px 0;
+        border-bottom: 1px solid #e9ecef;
+        
+        &:last-child {
+          border-bottom: none;
+        }
+        
+        .internal-label {
+          font-size: 13px;
+          color: #6c757d;
+          font-weight: 500;
+        }
+        
+        .internal-value {
+          font-size: 14px;
+          color: #212529;
+          font-weight: 500;
+        }
+        
+        .internal-badge {
+          padding: 4px 12px;
+          border-radius: 12px;
+          font-size: 12px;
+          font-weight: 500;
+          background: #e9ecef;
+          color: #495057;
+          
+          &.excellent {
+            background: #d4edda;
+            color: #155724;
+          }
+        }
+      }
+    }
+  }
+}
+
+// 加载状态
+.loading-container {
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  min-height: 100vh;
+  background: #f8f9fa;
+  
+  .loading-spinner {
+    text-align: center;
+    
+    .spinner {
+      width: 40px;
+      height: 40px;
+      border: 4px solid #e9ecef;
+      border-top: 4px solid #007bff;
+      border-radius: 50%;
+      animation: spin 1s linear infinite;
+      margin: 0 auto 16px;
+    }
+    
+    p {
+      color: #6c757d;
+      font-size: 14px;
+      margin: 0;
+    }
+  }
+}
+
+@keyframes spin {
+  0% { transform: rotate(0deg); }
+  100% { transform: rotate(360deg); }
+}
+
+// 错误状态
+.error-container {
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  min-height: 100vh;
+  background: #f8f9fa;
+  
+  .error-content {
+    text-align: center;
+    padding: 40px;
+    background: white;
+    border-radius: 12px;
+    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+    max-width: 400px;
+    
+    svg {
+      color: #dc3545;
+      margin-bottom: 16px;
+    }
+    
+    h3 {
+      font-size: 20px;
+      font-weight: 600;
+      color: #212529;
+      margin: 0 0 8px 0;
+    }
+    
+    p {
+      color: #6c757d;
+      margin: 0 0 24px 0;
+      line-height: 1.5;
+    }
+    
+    .btn {
+      padding: 10px 20px;
+      border: none;
+      border-radius: 6px;
+      font-size: 14px;
+      font-weight: 500;
+      cursor: pointer;
+      transition: all 0.2s;
+      
+      &.btn-primary {
+        background: #007bff;
+        color: white;
+        
+        &:hover {
+          background: #0056b3;
+        }
+      }
+    }
+  }
+}
+
+// 响应式设计
+@media (max-width: 768px) {
+  .case-detail-page {
+    .page-header {
+      padding: 16px;
+      flex-direction: column;
+      gap: 16px;
+      align-items: stretch;
+      
+      .header-left {
+        flex-direction: column;
+        gap: 12px;
+        align-items: flex-start;
+      }
+      
+      .header-actions {
+        justify-content: center;
+      }
+    }
+    
+    .main-content {
+      padding: 16px;
+      gap: 16px;
+    }
+    
+    .left-section {
+      .cover-image .cover-img {
+        height: 300px;
+      }
+      
+      .basic-info {
+        padding: 16px;
+        
+        .case-name {
+          font-size: 24px;
+        }
+        
+        .meta-grid {
+          grid-template-columns: 1fr;
+          gap: 16px;
+        }
+      }
+    }
+    
+    .right-section .info-section {
+      padding: 16px;
+    }
+  }
+}

+ 219 - 0
src/app/pages/customer-service/case-detail/case-detail.component.ts

@@ -0,0 +1,219 @@
+import { Component, OnInit } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { ActivatedRoute, Router } from '@angular/router';
+
+interface Case {
+  id: string;
+  name: string;
+  coverImage: string;
+  projectType: '工装' | '家装';
+  spaceType: '平层' | '复式' | '别墅' | '自建房';
+  renderingLevel: '高端' | '中端' | '低端';
+  designer: string;
+  team: string;
+  area: number;
+  styleTags: string[];
+  customerReview?: string;
+  viewCount: number;
+  shareCount: number;
+  favoriteCount: number;
+  isFavorite: boolean;
+  isExcellent: boolean;
+  createdAt: Date;
+}
+
+@Component({
+  selector: 'app-case-detail',
+  standalone: true,
+  imports: [CommonModule],
+  templateUrl: './case-detail.component.html',
+  styleUrls: ['./case-detail.component.scss']
+})
+export class CaseDetailComponent implements OnInit {
+  case: Case | null = null;
+  isInternalUser: boolean = true; // 可根据实际用户权限设置
+  isLoading: boolean = true;
+  caseId: string = '';
+
+  constructor(
+    private route: ActivatedRoute,
+    private router: Router
+  ) {}
+
+  ngOnInit() {
+    // 获取路由参数中的案例ID
+    this.route.paramMap.subscribe(params => {
+      this.caseId = params.get('id') || '';
+      if (this.caseId) {
+        this.loadCaseDetail(this.caseId);
+      }
+    });
+  }
+
+  private loadCaseDetail(caseId: string) {
+    this.isLoading = true;
+    
+    // 模拟API调用,实际项目中应该调用真实的API
+    setTimeout(() => {
+      this.case = this.generateMockCase(caseId);
+      this.isLoading = false;
+      
+      // 增加浏览次数
+      if (this.case) {
+        this.case.viewCount++;
+        this.recordBehavior('case_view', this.case.id, {
+          caseName: this.case.name,
+          designer: this.case.designer,
+          projectType: this.case.projectType,
+          spaceType: this.case.spaceType
+        });
+      }
+    }, 500);
+  }
+
+  private generateMockCase(caseId: string): Case {
+    const projectTypes: ('工装' | '家装')[] = ['工装', '家装'];
+    const spaceTypes: ('平层' | '复式' | '别墅' | '自建房')[] = ['平层', '复式', '别墅', '自建房'];
+    const renderingLevels: ('高端' | '中端' | '低端')[] = ['高端', '中端', '低端'];
+    const styles = ['现代', '中式', '欧式', '美式', '日式', '工业风', '极简风', '轻奢风'];
+    const designers = ['张三', '李四', '王五', '赵六', '钱七'];
+    const teams = ['设计一组', '设计二组', '设计三组', '设计四组'];
+
+    const projectType = projectTypes[Math.floor(Math.random() * projectTypes.length)];
+    const spaceType = spaceTypes[Math.floor(Math.random() * spaceTypes.length)];
+    const renderingLevel = renderingLevels[Math.floor(Math.random() * renderingLevels.length)];
+    const styleCount = Math.floor(Math.random() * 3) + 1;
+    const styleTags = Array.from({ length: styleCount }, () => 
+      styles[Math.floor(Math.random() * styles.length)]
+    );
+
+    return {
+      id: caseId,
+      name: `${projectType}${spaceType}设计案例 - ${caseId}`,
+      coverImage: this.generatePlaceholderImage(800, 600, caseId),
+      projectType,
+      spaceType,
+      renderingLevel,
+      designer: designers[Math.floor(Math.random() * designers.length)],
+      team: teams[Math.floor(Math.random() * teams.length)],
+      area: Math.floor(Math.random() * 200) + 50,
+      styleTags: [...new Set(styleTags)], // 去重
+      customerReview: Math.random() > 0.3 ? `客户非常满意这次${projectType}设计,${spaceType}空间利用得很合理,设计师的专业水平很高,整体效果超出预期。` : undefined,
+      viewCount: Math.floor(Math.random() * 1000),
+      shareCount: Math.floor(Math.random() * 100),
+      favoriteCount: Math.floor(Math.random() * 50),
+      isFavorite: Math.random() > 0.7,
+      isExcellent: Math.random() > 0.5,
+      createdAt: new Date(Date.now() - Math.floor(Math.random() * 365 * 24 * 60 * 60 * 1000))
+    };
+  }
+
+  private generatePlaceholderImage(width: number, height: number, seed: string): string {
+    return `https://picsum.photos/seed/${seed}/${width}/${height}`;
+  }
+
+  goBack() {
+    this.router.navigate(['/customer-service/case-library']);
+  }
+
+  toggleFavorite() {
+    if (!this.case) return;
+    
+    const wasLiked = this.case.isFavorite;
+    this.case.isFavorite = !this.case.isFavorite;
+    
+    if (this.case.isFavorite) {
+      this.case.favoriteCount++;
+      this.showToast('已收藏该案例', 'success');
+      
+      this.recordBehavior('case_favorite', this.case.id, {
+        action: 'add',
+        caseName: this.case.name,
+        designer: this.case.designer
+      });
+    } else {
+      this.case.favoriteCount = Math.max(0, this.case.favoriteCount - 1);
+      this.showToast('已取消收藏', 'info');
+      
+      this.recordBehavior('case_favorite', this.case.id, {
+        action: 'remove',
+        caseName: this.case.name,
+        designer: this.case.designer
+      });
+    }
+  }
+
+  shareCase() {
+    if (!this.case) return;
+    
+    this.case.shareCount++;
+    
+    // 生成分享链接
+    const shareLink = this.generateShareLink();
+    
+    // 复制到剪贴板
+    navigator.clipboard.writeText(shareLink).then(() => {
+      this.showToast('分享链接已复制到剪贴板', 'success');
+    }).catch(() => {
+      this.showToast('复制失败,请手动复制链接', 'error');
+    });
+    
+    this.recordBehavior('share', this.case.id, {
+      caseName: this.case.name,
+      designer: this.case.designer,
+      projectType: this.case.projectType
+    });
+  }
+
+  generateShareLink(): string {
+    if (!this.case) return '';
+    return `${window.location.origin}/customer-service/case-detail/${this.case.id}?from=share&designer=${encodeURIComponent(this.case.designer)}`;
+  }
+
+  getFormattedDate(date: Date): string {
+    return new Intl.DateTimeFormat('zh-CN', {
+      year: 'numeric',
+      month: 'long',
+      day: 'numeric'
+    }).format(new Date(date));
+  }
+
+  private recordBehavior(action: string, caseId: string, data?: any) {
+    // 模拟行为记录,实际项目中应该调用真实的API
+    console.log('行为记录:', {
+      action,
+      caseId,
+      data,
+      timestamp: new Date().toISOString(),
+      userAgent: navigator.userAgent
+    });
+  }
+
+  private showToast(message: string, type: 'success' | 'error' | 'info' = 'info') {
+    // 简单的toast实现,实际项目中可以使用更完善的toast库
+    const toast = document.createElement('div');
+    toast.className = `toast toast-${type}`;
+    toast.textContent = message;
+    toast.style.cssText = `
+      position: fixed;
+      top: 20px;
+      right: 20px;
+      padding: 12px 20px;
+      border-radius: 8px;
+      color: white;
+      font-size: 14px;
+      z-index: 10000;
+      animation: slideInRight 0.3s ease-out;
+      background: ${type === 'success' ? '#34c759' : type === 'error' ? '#ff3b30' : '#007aff'};
+    `;
+    
+    document.body.appendChild(toast);
+    
+    setTimeout(() => {
+      toast.style.animation = 'slideOutRight 0.3s ease-in';
+      setTimeout(() => {
+        document.body.removeChild(toast);
+      }, 300);
+    }, 3000);
+  }
+}

+ 10 - 0
src/app/pages/customer-service/case-library/case-library.html

@@ -242,6 +242,16 @@
               </div>
             </div>
           }
+
+          <!-- 操作按钮 -->
+          <div class="case-actions">
+            <button class="btn btn-outline" (click)="viewCaseDetail(caseItem)">
+              快速预览
+            </button>
+            <button class="btn btn-primary" (click)="navigateToCaseDetail(caseItem)">
+              查看详情
+            </button>
+          </div>
         </div>
       </div>
     } @empty {

+ 43 - 0
src/app/pages/customer-service/case-library/case-library.scss

@@ -525,6 +525,49 @@ $red-500: #ef4444;
             }
           }
         }
+
+        .case-actions {
+          display: flex;
+          gap: 8px;
+          margin-top: 16px;
+          padding-top: 16px;
+          border-top: 1px solid $border-color;
+
+          .btn {
+            flex: 1;
+            padding: 8px 16px;
+            border-radius: 6px;
+            font-size: 14px;
+            font-weight: 500;
+            text-align: center;
+            transition: all 0.2s ease;
+            cursor: pointer;
+            border: none;
+
+            &.btn-outline {
+              background: white;
+              border: 1px solid $border-color;
+              color: $text-secondary;
+
+              &:hover {
+                background: $gray-50;
+                border-color: $primary-color;
+                color: $primary-color;
+              }
+            }
+
+            &.btn-primary {
+              background: $primary-color;
+              color: white;
+
+              &:hover {
+                background: #0051a8;
+                transform: translateY(-1px);
+                box-shadow: 0 2px 8px rgba(0, 102, 204, 0.3);
+              }
+            }
+          }
+        }
       }
     }
   }

+ 24 - 1
src/app/pages/customer-service/case-library/case-library.ts

@@ -2,6 +2,7 @@ import { Component, OnInit } from '@angular/core';
 import { CommonModule } from '@angular/common';
 import { FormsModule, ReactiveFormsModule } from '@angular/forms';
 import { FormControl } from '@angular/forms';
+import { Router } from '@angular/router';
 import { debounceTime, distinctUntilChanged } from 'rxjs/operators';
 import { CaseDetailPanelComponent } from './case-detail-panel.component';
 
@@ -81,6 +82,8 @@ export class CaseLibrary implements OnInit {
   private pageStartTime = Date.now();
   private caseViewStartTimes = new Map<string, number>();
 
+  constructor(private router: Router) {}
+
   ngOnInit() {
     this.initializeData();
     this.setupFilterListeners();
@@ -335,6 +338,26 @@ export class CaseLibrary implements OnInit {
     this.selectedCase = caseItem;
   }
 
+  // 跳转到独立的案例详情页面
+  navigateToCaseDetail(caseItem: Case) {
+    // 记录案例查看开始时间
+    this.caseViewStartTimes.set(caseItem.id, Date.now());
+    
+    // 增加浏览次数
+    caseItem.viewCount++;
+    
+    // 记录浏览行为
+    this.recordBehavior('case_view', caseItem.id, {
+      caseName: caseItem.name,
+      designer: caseItem.designer,
+      projectType: caseItem.projectType,
+      spaceType: caseItem.spaceType
+    });
+    
+    // 跳转到独立的案例详情页面
+    this.router.navigate(['/customer-service/case-detail', caseItem.id]);
+  }
+
   closeCaseDetail() {
     // 记录案例查看时长
     if (this.selectedCase) {
@@ -414,7 +437,7 @@ export class CaseLibrary implements OnInit {
   }
 
   generateShareLink(caseItem: Case): string {
-    return `${window.location.origin}/case/${caseItem.id}?from=share&designer=${encodeURIComponent(caseItem.designer)}`;
+    return `${window.location.origin}/customer-service/case-detail/${caseItem.id}?from=share&designer=${encodeURIComponent(caseItem.designer)}`;
   }
 
   copyShareLink() {

+ 16 - 0
src/app/pages/customer-service/consultation-order/consultation-order-dialog.component.html

@@ -0,0 +1,16 @@
+<div class="consultation-order-dialog">
+  <!-- 弹窗头部 -->
+  <div class="dialog-header">
+    <h2 class="dialog-title">创建新项目</h2>
+    <button class="close-button" (click)="onClose()" mat-icon-button>
+      <mat-icon>close</mat-icon>
+    </button>
+  </div>
+
+  <!-- 弹窗内容 -->
+  <div class="dialog-content">
+    <app-consultation-order 
+      (orderCreated)="onOrderCreated($event)">
+    </app-consultation-order>
+  </div>
+</div>

+ 111 - 0
src/app/pages/customer-service/consultation-order/consultation-order-dialog.component.scss

@@ -0,0 +1,111 @@
+@use '../../../shared/styles/variables';
+@use '../../../shared/styles/ios-theme' as ios;
+
+.consultation-order-dialog {
+  // 弹窗容器样式
+  .mat-mdc-dialog-container .mdc-dialog__surface {
+    border-radius: ios.$ios-radius-lg;
+    background: ios.$ios-card-background;
+    box-shadow: ios.$ios-shadow-lg;
+    border: 1px solid ios.$ios-border;
+    min-width: 800px;
+    max-width: 1000px;
+    max-height: 90vh;
+    overflow: hidden;
+  }
+
+  .dialog-header {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    padding: ios.$ios-spacing-lg ios.$ios-spacing-lg ios.$ios-spacing-md;
+    border-bottom: 1px solid ios.$ios-border;
+    background: ios.$ios-background;
+
+    .dialog-title {
+      color: ios.$ios-text-primary;
+      font-weight: ios.$ios-font-weight-semibold;
+      font-family: ios.$ios-font-family;
+      font-size: ios.$ios-font-size-lg;
+      margin: 0;
+    }
+
+    .close-button {
+      color: ios.$ios-text-secondary;
+      transition: color 0.3s ease;
+
+      &:hover {
+        color: ios.$ios-primary;
+      }
+    }
+  }
+
+  .dialog-content {
+    padding: 0;
+    max-height: calc(90vh - 80px);
+    overflow-y: auto;
+
+    // 确保咨询订单组件内部样式正常
+    app-consultation-order {
+      display: block;
+      height: 100%;
+
+      ::ng-deep {
+        .consultation-order-container {
+          height: 100%;
+          border-radius: 0;
+          box-shadow: none;
+          margin: 0;
+
+          .dashboard-header {
+            border-radius: 0;
+            margin: 0;
+          }
+
+          .dashboard-content {
+            max-height: calc(90vh - 140px);
+            overflow-y: auto;
+            padding: ios.$ios-spacing-md;
+          }
+        }
+      }
+    }
+  }
+
+  // 响应式设计
+  @media (max-width: 1024px) {
+    .mat-mdc-dialog-container .mdc-dialog__surface {
+      min-width: 90vw;
+      max-width: 95vw;
+      margin: ios.$ios-spacing-md;
+    }
+  }
+
+  @media (max-width: 768px) {
+    .mat-mdc-dialog-container .mdc-dialog__surface {
+      min-width: 95vw;
+      max-width: 95vw;
+      margin: ios.$ios-spacing-sm;
+    }
+
+    .dialog-header {
+      padding: ios.$ios-spacing-md;
+
+      .dialog-title {
+        font-size: ios.$ios-font-size-md;
+      }
+    }
+
+    .dialog-content {
+      app-consultation-order {
+        ::ng-deep {
+          .consultation-order-container {
+            .dashboard-content {
+              padding: ios.$ios-spacing-sm;
+            }
+          }
+        }
+      }
+    }
+  }
+}

+ 40 - 0
src/app/pages/customer-service/consultation-order/consultation-order-dialog.component.ts

@@ -0,0 +1,40 @@
+import { Component, Inject, Output, EventEmitter } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { MatButtonModule } from '@angular/material/button';
+import { MatDialogModule, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
+import { MatIconModule } from '@angular/material/icon';
+import { ConsultationOrder } from './consultation-order';
+
+@Component({
+  selector: 'app-consultation-order-dialog',
+  standalone: true,
+  imports: [
+    CommonModule,
+    MatButtonModule,
+    MatDialogModule,
+    MatIconModule,
+    ConsultationOrder
+  ],
+  templateUrl: './consultation-order-dialog.component.html',
+  styleUrls: ['./consultation-order-dialog.component.scss']
+})
+export class ConsultationOrderDialogComponent {
+  @Output() orderCreated = new EventEmitter<any>();
+
+  constructor(
+    public dialogRef: MatDialogRef<ConsultationOrderDialogComponent>,
+    @Inject(MAT_DIALOG_DATA) public data: any
+  ) {}
+
+  onClose(): void {
+    this.dialogRef.close();
+  }
+
+  // 监听订单创建成功事件
+  onOrderCreated(orderData: any): void {
+    // 发出订单创建事件
+    this.orderCreated.emit(orderData);
+    // 关闭弹窗并返回订单数据
+    this.dialogRef.close(orderData);
+  }
+}

+ 81 - 109
src/app/pages/customer-service/consultation-order/consultation-order.html

@@ -1,63 +1,34 @@
 <div class="consultation-order-container">
   <!-- Dashboard风格页面头部 -->
   <header class="page-header">
-    <h1>创建订单</h1>
-    <div class="header-meta">通过小程序或人工方式创建新订单</div>
+    <!-- 返回按钮 -->
+    <button class="back-button" (click)="goBackToProjectList()" title="返回项目列表">
+      <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
+        <path d="m15 18-6-6 6-6"/>
+      </svg>
+      <span>返回</span>
+    </button>
+    
+    <div class="header-content">
+      <h1>创建订单</h1>
+      <div class="header-meta">通过小程序或人工方式创建新订单</div>
+    </div>
     
     <!-- 成功提示 -->
-    <div *ngIf="showSuccessMessage()" class="success-toast">
-      <div class="toast-content">
-        <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
-          <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>
+    @if (showSuccessMessage()) {
+      <div class="success-toast">
+        <div class="toast-content">
+          <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
+            <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>
+        </div>
       </div>
-    </div>
+    }
   </header>
 
-  <!-- 订单创建方式选择 -->
-  <section class="creation-method-section">
-    <div class="method-selector">
-      <div class="method-title">订单创建方式</div>
-      <div class="method-options">
-        <button 
-          class="method-option"
-          [class.active]="orderCreationMethod() === 'miniprogram'"
-          (click)="switchOrderCreationMethod('miniprogram')"
-        >
-          <div class="option-icon miniprogram-icon">
-            <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
-              <rect x="2" y="3" width="20" height="14" rx="2" ry="2"></rect>
-              <line x1="8" y1="21" x2="16" y2="21"></line>
-              <line x1="12" y1="17" x2="12" y2="21"></line>
-            </svg>
-          </div>
-          <div class="option-content">
-            <div class="option-title">小程序创建</div>
-            <div class="option-desc">从小程序数据库同步信息</div>
-          </div>
-          <div class="option-badge">推荐</div>
-        </button>
-        
-        <button 
-          class="method-option"
-          [class.active]="orderCreationMethod() === 'manual'"
-          (click)="switchOrderCreationMethod('manual')"
-        >
-          <div class="option-icon manual-icon">
-            <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
-              <path d="M17 3a2.828 2.828 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5L17 3z"></path>
-            </svg>
-          </div>
-          <div class="option-content">
-            <div class="option-title">人工手动创建</div>
-            <div class="option-desc">手动填写客户和项目信息</div>
-          </div>
-        </button>
-      </div>
-    </div>
-  </section>
+
 
   <!-- 主要内容区域 -->
   <div class="main-content">
@@ -105,17 +76,13 @@
               <button 
                 class="search-action-btn"
                 (click)="quickFillCustomerInfo(searchKeyword())"
-                [disabled]="!searchKeyword().trim() || isSyncing()"
+                [disabled]="!searchKeyword().trim()"
               >
-                @if (isSyncing()) {
-                  <mat-spinner diameter="16"></mat-spinner>
-                } @else {
-                  <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
-                    <path d="M9 11l3 3 8-8"></path>
-                    <path d="M21 12c0 4.97-4.03 9-9 9s-9-4.03-9-9 4.03-9 9-9c1.51 0 2.93.37 4.18 1.03"></path>
-                  </svg>
-                  匹配
-                }
+                <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
+                  <path d="M9 11l3 3 8-8"></path>
+                  <path d="M21 12c0 4.97-4.03 9-9 9s-9-4.03-9-9 4.03-9 9-9c1.51 0 2.93.37 4.18 1.03"></path>
+                </svg>
+                匹配
               </button>
             </div>
             
@@ -192,30 +159,12 @@
               重新选择
             </button>
           }
-          @if (orderCreationMethod() === 'miniprogram') {
-            <button 
-              class="action-btn primary" 
-              (click)="syncMiniprogramCustomerInfo()"
-              [disabled]="isSyncing()"
-            >
-              @if (isSyncing()) {
-                <mat-spinner diameter="16"></mat-spinner>
-              } @else {
-                <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
-                  <polyline points="23 4 23 10 17 10"></polyline>
-                  <polyline points="1 20 1 14 7 14"></polyline>
-                  <path d="m3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"></path>
-                </svg>
-              }
-              {{ isSyncing() ? '同步中...' : '同步小程序' }}
-            </button>
-          }
         </div>
       </div>
     </section>
 
     <!-- 新客户快速填写表单 -->
-    @if (!selectedCustomer() && orderCreationMethod() === 'manual') {
+    @if (!selectedCustomer()) {
       <section class="new-customer-form">
         <div class="form-header">
           <h3>新客户信息</h3>
@@ -237,7 +186,8 @@
     }
 
     <!-- 项目需求卡片(可折叠) -->
-    <section class="info-card requirement-card" *ngIf="isRequirementCardExpanded()">
+    @if (isRequirementCardExpanded()) {
+      <section class="info-card requirement-card">
       <div class="card-header">
         <div class="header-left">
           <div class="icon-wrapper requirement-icon">
@@ -259,45 +209,59 @@
             </svg>
             收起
           </button>
-          <button class="btn-primary btn-sm" (click)="syncProjectInfo()" [disabled]="isSyncing()">
-            <mat-spinner *ngIf="isSyncing()" diameter="16"></mat-spinner>
-            <span *ngIf="!isSyncing()">从聊天记录提取</span>
-          </button>
         </div>
       </div>
 
       <div class="card-content">
         <form [formGroup]="requirementForm" class="requirement-form">
-          <div class="form-grid">
+          <!-- 第一行:装修类型、首付款、首稿时间 -->
+          <div class="form-row">
             <div class="form-field">
-              <label for="style" class="field-label">装修风格 <span class="required">*</span></label>
-              <select id="style" formControlName="style" class="field-select">
-                <option value="">请选择装修风格</option>
-                <option *ngFor="let style of styleOptions" [value]="style">{{ style }}</option>
+              <label for="decorationType" class="field-label">装修类型 <span class="required">*</span></label>
+              <select id="decorationType" formControlName="decorationType" class="field-select">
+                <option value="">请选择装修类型</option>
+                <option value="家装">家装</option>
+                <option value="工装">工装</option>
               </select>
             </div>
             <div class="form-field">
-              <label for="projectGroup" class="field-label">项目小组 <span class="required">*</span></label>
-              <select id="projectGroup" formControlName="projectGroup" class="field-select">
-                <option value="">请选择项目小组</option>
-                <option *ngFor="let group of projectGroupOptions" [value]="group">{{ group }}</option>
-              </select>
+              <label for="downPayment" class="field-label">首付款 <span class="required">*</span></label>
+              <div class="input-with-unit">
+                <input type="number" id="downPayment" formControlName="downPayment" placeholder="请输入首付款金额" class="field-input">
+                <span class="input-unit">元</span>
+              </div>
             </div>
             <div class="form-field">
               <label for="firstDraftDate" class="field-label">首稿时间 <span class="required">*</span></label>
               <input type="date" id="firstDraftDate" formControlName="firstDraftDate" class="field-input">
             </div>
+          </div>
+          
+          <!-- 第二行:装修风格、项目小组(选填) -->
+          <div class="form-row">
             <div class="form-field">
-              <label for="downPayment" class="field-label">首付款 <span class="required">*</span></label>
-              <div class="input-with-unit">
-                <input type="number" id="downPayment" formControlName="downPayment" placeholder="请输入首付款金额" class="field-input">
-                <span class="input-unit">元</span>
-              </div>
+              <label for="style" class="field-label">装修风格</label>
+              <select id="style" formControlName="style" class="field-select">
+                <option value="">请选择装修风格(选填)</option>
+                @for (style of styleOptions; track style) {
+                  <option [value]="style">{{ style }}</option>
+                }
+              </select>
+            </div>
+            <div class="form-field">
+              <label for="projectGroup" class="field-label">项目小组</label>
+              <select id="projectGroup" formControlName="projectGroup" class="field-select">
+                <option value="">请选择项目小组(选填)</option>
+                @for (group of projectGroupOptions; track group) {
+                  <option [value]="group">{{ group }}</option>
+                }
+              </select>
             </div>
           </div>
         </form>
       </div>
     </section>
+    }
 
 
 
@@ -308,14 +272,22 @@
           (click)="submitForm()"
           [disabled]="isSubmitting() || !customerForm.valid"
         >
-          <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" *ngIf="!isSubmitting()">
-            <path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"></path>
-          </svg>
-          <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" *ngIf="isSubmitting()" class="animate-spin">
-            <path d="M21 12a9 9 0 1 1-6.219-8.56"></path>
-          </svg>
-          <span *ngIf="!isSubmitting()">创建项目</span>
-          <span *ngIf="isSubmitting()">提交中...</span>
+          @if (!isSubmitting()) {
+            <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
+              <path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"></path>
+            </svg>
+          }
+          @if (isSubmitting()) {
+            <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class="animate-spin">
+              <path d="M21 12a9 9 0 1 1-6.219-8.56"></path>
+            </svg>
+          }
+          @if (!isSubmitting()) {
+            <span>创建项目</span>
+          }
+          @if (isSubmitting()) {
+            <span>提交中...</span>
+          }
         </button>
       </div>
     </section>

+ 69 - 2
src/app/pages/customer-service/consultation-order/consultation-order.scss

@@ -623,9 +623,10 @@ $card-padding: 16px;
 
 // iOS风格页面头部样式
 .page-header {
-  text-align: center;
+  display: flex;
+  align-items: center;
   margin-bottom: 32px;
-  padding: 32px 0;
+  padding: 32px;
   background: linear-gradient(135deg, #ffffff 0%, #f8f9fa 100%);
   border-radius: 16px;
   box-shadow: 0 2px 12px rgba(0, 0, 0, 0.04);
@@ -641,6 +642,54 @@ $card-padding: 16px;
     height: 2px;
     background: linear-gradient(90deg, #007AFF 0%, #34C759 100%);
   }
+
+  // 返回按钮样式
+  .back-button {
+    display: flex;
+    align-items: center;
+    gap: 8px;
+    padding: 12px 16px;
+    background: rgba(0, 122, 255, 0.1);
+    border: 1px solid rgba(0, 122, 255, 0.2);
+    border-radius: 12px;
+    color: #007AFF;
+    font-size: 14px;
+    font-weight: 600;
+    cursor: pointer;
+    transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
+    margin-right: 24px;
+    min-width: 80px;
+    
+    &:hover {
+      background: rgba(0, 122, 255, 0.15);
+      border-color: rgba(0, 122, 255, 0.3);
+      transform: translateY(-1px);
+      box-shadow: 0 4px 12px rgba(0, 122, 255, 0.2);
+    }
+    
+    &:active {
+      transform: translateY(0);
+      box-shadow: 0 2px 6px rgba(0, 122, 255, 0.15);
+    }
+    
+    svg {
+      transition: transform 0.3s ease;
+    }
+    
+    &:hover svg {
+      transform: translateX(-2px);
+    }
+    
+    span {
+      font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Text', 'PingFang SC', sans-serif;
+    }
+  }
+
+  // 头部内容区域
+  .header-content {
+    flex: 1;
+    text-align: center;
+  }
   
   h1 {
     font-size: 36px;
@@ -1212,6 +1261,24 @@ $card-padding: 16px;
   }
 }
 
+// 表单行布局 - 支持多行表单
+.form-row {
+  display: grid;
+  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
+  gap: 20px;
+  margin-bottom: 20px;
+  
+  &:last-child {
+    margin-bottom: 0;
+  }
+  
+  @media (max-width: 768px) {
+    grid-template-columns: 1fr;
+    gap: 16px;
+    margin-bottom: 16px;
+  }
+}
+
 // iOS风格表单字段样式
 .form-field {
   display: flex;

+ 58 - 103
src/app/pages/customer-service/consultation-order/consultation-order.ts

@@ -1,7 +1,7 @@
-import { Component, signal, Inject } from '@angular/core';
-import { CommonModule, NgIf, NgFor } from '@angular/common';
+import { Component, signal, Inject, Output, EventEmitter } from '@angular/core';
+import { CommonModule } from '@angular/common';
 import { FormsModule, ReactiveFormsModule, FormBuilder, FormGroup, Validators } from '@angular/forms';
-import { RouterModule } from '@angular/router';
+import { Router, RouterModule } from '@angular/router';
 import { ProjectService } from '../../../services/project.service';
 import { MatChipInputEvent } from '@angular/material/chips';
 import { COMMA, ENTER } from '@angular/cdk/keycodes';
@@ -71,8 +71,6 @@ const PREFERENCE_TAG_OPTIONS = [
   standalone: true,
   imports: [
     CommonModule,
-    NgIf,
-    NgFor,
     FormsModule,
     ReactiveFormsModule,
     RouterModule,
@@ -100,15 +98,14 @@ export class ConsultationOrder {
   isSubmitting = signal(false);
   // 成功提示显示状态
   showSuccessMessage = signal(false);
-  // 订单创建方式
-  orderCreationMethod = signal<'miniprogram' | 'manual'>('miniprogram');
-  // 同步状态
-  isSyncing = signal(false);
   // 下单时间(自动生成)
   orderTime = signal<string>('');
   // 项目需求卡片展开状态
   isRequirementCardExpanded = signal(false);
 
+  // 订单创建成功事件
+  @Output() orderCreated = new EventEmitter<any>();
+
   // 需求表单
   requirementForm: FormGroup;
   // 客户表单
@@ -159,24 +156,24 @@ export class ConsultationOrder {
     private fb: FormBuilder,
     private projectService: ProjectService,
     private snackBar: MatSnackBar,
-    private dialog: MatDialog
+    private dialog: MatDialog,
+    private router: Router
   ) {
     // 初始化需求表单(保留兼容性)
     this.requirementForm = this.fb.group({
-      style: ['', Validators.required],
+      decorationType: ['', Validators.required], // 装修类型(必填)
+      downPayment: ['', [Validators.required, Validators.min(0)]], // 首付款(必填)
+      firstDraftDate: ['', Validators.required], // 首稿时间(必填)
+      style: [''], // 装修风格(选填)
+      projectGroup: [''], // 项目小组(选填)
       budget: ['', Validators.required],
       area: ['', [Validators.required, Validators.min(1)]],
       houseType: [''], // 改为非必填
       floor: ['', Validators.min(1)],
-      decorationType: [''], // 改为非必填
       preferredDesigner: [''],
       specialRequirements: [''],
       referenceCases: [[]],
-      // 新增字段
-      projectGroup: ['', Validators.required], // 项目小组
-      downPayment: ['', [Validators.required, Validators.min(0)]], // 首付款
-      priceDetails: [''], // 价格明细
-      firstDraftDate: ['', Validators.required] // 首稿时间
+      priceDetails: [''] // 价格明细
     });
 
     // 初始化客户表单
@@ -405,10 +402,23 @@ export class ConsultationOrder {
         this.isSubmitting.set(false);
         this.showSuccessMessage.set(true);
         
-        // 3秒后隐藏成功提示
+        // 1秒后隐藏成功提示并跳转
         setTimeout(() => {
           this.showSuccessMessage.set(false);
-        }, 3000);
+          
+          // 发出订单创建成功事件,用于关闭弹窗
+          const orderData = {
+            orderId: 'mock-9',
+            customerName: formData.customerInfo.name,
+            projectId: 'mock-9'
+          };
+          this.orderCreated.emit(orderData);
+          
+          // 跳转到设计师项目详情页面,传递客服角色标识
+          this.router.navigate(['/designer/project-detail/mock-9'], { 
+            queryParams: { role: 'customer-service' } 
+          });
+        }, 1000);
       }, 1500);
     }
   }
@@ -467,7 +477,24 @@ export class ConsultationOrder {
         if (res?.success) {
           this.showSuccessMessage.set(true);
           this.snackBar.open('项目创建成功', '关闭', { duration: 2000 });
-          setTimeout(() => this.showSuccessMessage.set(false), 2500);
+          
+          // 延迟跳转到项目详情页面
+          setTimeout(() => {
+            this.showSuccessMessage.set(false);
+            
+            // 发出订单创建成功事件,用于关闭弹窗
+            const orderData = {
+              orderId: 'mock-9',
+              customerName: nameCtrl.value,
+              projectId: 'mock-9'
+            };
+            this.orderCreated.emit(orderData);
+            
+            // 跳转到设计师项目详情页面,传递客服角色标识
+            this.router.navigate(['/designer/project-detail/mock-9'], { 
+              queryParams: { role: 'customer-service' } 
+            });
+          }, 1000);
         } else {
           this.snackBar.open('创建失败,请稍后重试', '关闭', { duration: 3000 });
         }
@@ -530,86 +557,7 @@ export class ConsultationOrder {
     });
   }
 
-  /**
-   * 切换订单创建方式
-   */
-  switchOrderCreationMethod(method: 'miniprogram' | 'manual') {
-    this.orderCreationMethod.set(method);
-    if (method === 'miniprogram') {
-      // 切换到小程序模式时,清空手动填写的信息
-      this.clearSelectedCustomer();
-    }
-  }
-
-  /**
-   * 同步小程序客户信息
-   */
-  syncMiniprogramCustomerInfo() {
-    this.isSyncing.set(true);
-    
-    // 模拟从小程序数据库同步客户信息
-    setTimeout(() => {
-      // 这里应该调用实际的API来获取小程序客户信息
-      const mockCustomerData = {
-        id: 'mp_' + Date.now(),
-        name: '张三',
-        phone: '13800138000',
-        wechat: 'zhangsan_wx',
-        customerType: '新客户',
-        source: '小程序注册',
-        avatar: '',
-        demandType: 'comprehensive',
-        preferenceTags: ['现代简约', '环保材料'],
-        followUpStatus: 'quotation'
-      };
-
-      this.selectedCustomer.set(mockCustomerData);
-      this.customerForm.patchValue({
-        name: mockCustomerData.name,
-        phone: mockCustomerData.phone,
-        wechat: mockCustomerData.wechat,
-        customerType: mockCustomerData.customerType,
-        source: mockCustomerData.source,
-        demandType: mockCustomerData.demandType,
-        followUpStatus: mockCustomerData.followUpStatus
-      });
-
-      this.preferenceTags = [...mockCustomerData.preferenceTags];
-      this.isSyncing.set(false);
-      this.snackBar.open('客户信息同步成功', '确定', { duration: 2000 });
-    }, 1500);
-  }
-
-  /**
-   * 同步项目信息
-   */
-  syncProjectInfo() {
-    this.isSyncing.set(true);
-    
-    // 模拟从数据库同步项目信息
-    setTimeout(() => {
-      // 这里应该调用实际的API来获取项目信息
-      const mockProjectData = {
-        style: '现代简约',
-        budget: '10-20万',
-        area: 120,
-        houseType: '三室两厅',
-        floor: 15,
-        decorationType: '全包',
-        preferredDesigner: '张设计师',
-        specialRequirements: '需要环保材料,注重收纳空间',
-        // 新增字段的模拟数据
-        projectGroup: '设计一组',
-        downPayment: 50000,
-        priceDetails: '设计费:15000元\n施工费:180000元\n主材费:120000元\n软装费:35000元',
-        firstDraftDate: new Date(Date.now() + 14 * 24 * 60 * 60 * 1000).toISOString().split('T')[0] // 14天后
-      };
 
-      this.requirementForm.patchValue(mockProjectData);
-      this.isSyncing.set(false);
-      this.snackBar.open('项目信息同步成功', '确定', { duration: 2000 });
-    }, 1500);
-  }
 
   /**
    * 快速填写客户信息(通过姓名或手机号)
@@ -619,8 +567,6 @@ export class ConsultationOrder {
       return;
     }
 
-    this.isSyncing.set(true);
-    
     // 模拟根据姓名或手机号查询客户信息
     setTimeout(() => {
       // 这里应该调用实际的API来查询客户信息
@@ -649,7 +595,6 @@ export class ConsultationOrder {
       });
 
       this.preferenceTags = [...mockCustomer.preferenceTags];
-      this.isSyncing.set(false);
       this.snackBar.open('客户信息填写完成', '确定', { duration: 2000 });
     }, 1000);
   }
@@ -731,7 +676,9 @@ export class ConsultationOrder {
     const orderData = {
       customer: this.customerForm.value,
       project: this.projectForm.value,
-      orderTime: this.orderTime()
+      orderTime: this.orderTime(),
+      orderId: 'mock-' + Date.now(), // 模拟订单ID
+      status: 'created'
     };
 
     // 模拟提交
@@ -745,10 +692,18 @@ export class ConsultationOrder {
         verticalPosition: 'top'
       });
 
+      // 发射订单创建成功事件
+      this.orderCreated.emit(orderData);
+
       // 3秒后隐藏成功消息
       setTimeout(() => {
         this.showSuccessMessage.set(false);
       }, 3000);
     }, 2000);
   }
+
+  // 返回项目列表页面
+  goBackToProjectList() {
+    this.router.navigate(['/customer-service/project-list']);
+  }
 }

+ 41 - 31
src/app/pages/customer-service/consultation-order/project-group-dialog.component.ts

@@ -26,40 +26,50 @@ import { MatIconModule } from '@angular/material/icon';
     <h2 mat-dialog-title>创建项目群</h2>
     <mat-dialog-content>
       <div class="dialog-content">
-        <div class="customer-info" *ngIf="data.selectedCustomer">
-          <h3>客户信息</h3>
-          <p><strong>姓名:</strong> {{ data.selectedCustomer.name }}</p>
-          <p><strong>电话:</strong> {{ data.selectedCustomer.phone }}</p>
-          <p><strong>地址:</strong> {{ data.selectedCustomer.address }}</p>
-        </div>
-        <div class="no-customer" *ngIf="!data.selectedCustomer">
-          <p class="warning-message">请先选择客户</p>
-        </div>
-        
-        <div class="group-details" *ngIf="data.selectedCustomer">
-          <h3>项目群信息</h3>
-          <div class="form-group">
-            <label>需求类型</label>
-            <p>{{ data.demandType || '未设置' }}</p>
-          </div>
-          
-          <div class="form-group" *ngIf="data.preferenceTags && data.preferenceTags.length > 0">
-            <label>偏好标签</label>
-            <mat-chip-grid>
-              <mat-chip *ngFor="let tag of data.preferenceTags">{{ tag }}</mat-chip>
-            </mat-chip-grid>
+        @if (data.selectedCustomer) {
+          <div class="customer-info">
+            <h3>客户信息</h3>
+            <p><strong>姓名:</strong> {{ data.selectedCustomer.name }}</p>
+            <p><strong>电话:</strong> {{ data.selectedCustomer.phone }}</p>
+            <p><strong>地址:</strong> {{ data.selectedCustomer.address }}</p>
           </div>
-          
-          <div class="form-group">
-            <label>跟进状态</label>
-            <p>{{ data.followUpStatus || '未设置' }}</p>
+        }
+        @if (!data.selectedCustomer) {
+          <div class="no-customer">
+            <p class="warning-message">请先选择客户</p>
           </div>
-          
-          <div class="form-group">
-            <label>备注</label>
-            <textarea [(ngModel)]="remarks" rows="3" placeholder="输入群聊备注..."></textarea>
+        }
+        
+        @if (data.selectedCustomer) {
+          <div class="group-details">
+            <h3>项目群信息</h3>
+            <div class="form-group">
+              <label>需求类型</label>
+              <p>{{ data.demandType || '未设置' }}</p>
+            </div>
+            
+            @if (data.preferenceTags && data.preferenceTags.length > 0) {
+              <div class="form-group">
+                <label>偏好标签</label>
+                <mat-chip-grid>
+                  @for (tag of data.preferenceTags; track tag) {
+                    <mat-chip>{{ tag }}</mat-chip>
+                  }
+                </mat-chip-grid>
+              </div>
+            }
+            
+            <div class="form-group">
+              <label>跟进状态</label>
+              <p>{{ data.followUpStatus || '未设置' }}</p>
+            </div>
+            
+            <div class="form-group">
+              <label>备注</label>
+              <textarea [(ngModel)]="remarks" rows="3" placeholder="输入群聊备注..."></textarea>
+            </div>
           </div>
-        </div>
+        }
       </div>
     </mat-dialog-content>
     <mat-dialog-actions align="end">

+ 25 - 0
src/app/pages/customer-service/create-project-modal/create-project-modal.component.html

@@ -0,0 +1,25 @@
+<!-- 创建项目模态框组件 -->
+<div class="create-project-modal">
+  <div class="modal-header">
+    <h2>创建新项目</h2>
+    <button type="button" class="close-btn" (click)="onClose()">
+      <span>&times;</span>
+    </button>
+  </div>
+  
+  <div class="modal-body">
+    <!-- 咨询订单组件 -->
+    <app-consultation-order
+      (orderCreated)="onOrderCreated($event)">
+    </app-consultation-order>
+  </div>
+  
+  <div class="modal-footer">
+    <button type="button" class="btn btn-secondary" (click)="onClose()">
+      取消
+    </button>
+    <button type="button" class="btn btn-primary" (click)="onSave()">
+      保存
+    </button>
+  </div>
+</div>

+ 6 - 0
src/app/pages/customer-service/create-project-modal/create-project-modal.component.scss

@@ -0,0 +1,6 @@
+/* 这个样式文件只是为了解决编译错误而创建 */
+/* 实际样式应该使用 consultation-order-dialog 的样式 */
+
+:host {
+  display: block;
+}

+ 30 - 0
src/app/pages/customer-service/create-project-modal/create-project-modal.component.ts

@@ -0,0 +1,30 @@
+import { Component, Output, EventEmitter } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { ConsultationOrder } from '../consultation-order/consultation-order';
+
+@Component({
+  selector: 'app-create-project-modal',
+  standalone: true,
+  imports: [
+    CommonModule,
+    ConsultationOrder
+  ],
+  templateUrl: './create-project-modal.component.html',
+  styleUrls: ['./create-project-modal.component.scss']
+})
+export class CreateProjectModalComponent {
+  @Output() modalClosed = new EventEmitter<void>();
+  @Output() projectCreated = new EventEmitter<any>();
+
+  onClose(): void {
+    this.modalClosed.emit();
+  }
+
+  onSave(): void {
+    // 保存逻辑
+  }
+
+  onOrderCreated(orderData: any): void {
+    this.projectCreated.emit(orderData);
+  }
+}

+ 1 - 6
src/app/pages/customer-service/customer-service-layout/customer-service-layout.html

@@ -88,12 +88,7 @@
         </svg>
         <span>工作台</span>
       </a>
-      <a routerLink="/customer-service/consultation-order" class="nav-item" routerLinkActive="active">
-        <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor">
-          <path d="M16 3h5v5M8 3H3v5M12 19l-7-7 7-7M16 21h5v-5M8 21H3v-5"></path>
-        </svg>
-        <span>创建订单</span>
-      </a>
+
       <a routerLink="/customer-service/project-list" class="nav-item" routerLinkActive="active">
         <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor">
           <line x1="8" y1="6" x2="21" y2="6"></line>

+ 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;
+      }
     }
   }
 }

+ 27 - 2
src/app/pages/customer-service/customer-service-layout/customer-service-layout.ts

@@ -1,4 +1,4 @@
-import { Component, signal } from '@angular/core';
+import { Component, signal, OnInit, OnDestroy } from '@angular/core';
 import { CommonModule } from '@angular/common';
 import { Router, RouterOutlet, RouterLinkActive, RouterLink } from '@angular/router';
 import { FormsModule } from '@angular/forms';
@@ -10,7 +10,7 @@ import { FormsModule } from '@angular/forms';
   templateUrl: './customer-service-layout.html',
   styleUrl: './customer-service-layout.scss'
 }) 
-export class CustomerServiceLayout {
+export class CustomerServiceLayout implements OnInit, OnDestroy {
   sidebarOpen = true;
   searchTerm = '';
   currentDate = new Date();
@@ -25,6 +25,31 @@ export class CustomerServiceLayout {
 
   constructor(private router: Router) {}
 
+  ngOnInit() {
+    // 监听来自iframe的消息
+    window.addEventListener('message', this.handleIframeMessage.bind(this));
+  }
+
+  ngOnDestroy() {
+    // 清理事件监听器
+    window.removeEventListener('message', this.handleIframeMessage.bind(this));
+  }
+
+  // 处理来自iframe的消息
+  private handleIframeMessage(event: MessageEvent) {
+    // 验证消息来源(可以根据需要添加更严格的验证)
+    if (event.origin !== window.location.origin) {
+      return;
+    }
+
+    // 处理导航消息
+    if (event.data && event.data.type === 'navigate') {
+      const { url } = event.data;
+      console.log('收到iframe导航请求:', url);
+      this.router.navigateByUrl(url);
+    }
+  }
+
   toggleSidebar() {
     this.sidebarOpen = !this.sidebarOpen;
   }

+ 5 - 0
src/app/pages/customer-service/customer-service.routes.ts

@@ -3,6 +3,7 @@ import { Dashboard } from './dashboard/dashboard';
 import { ConsultationListComponent } from './dashboard/pages/consultation-list/consultation-list.component';
 import { AssignmentListComponent } from './dashboard/pages/assignment-list/assignment-list.component';
 import { ExceptionListComponent } from './dashboard/pages/exception-list/exception-list.component';
+import { AfterSalesComponent } from './dashboard/pages/after-sales/after-sales.component';
 
 export const CUSTOMER_SERVICE_ROUTES: Routes = [
   {
@@ -22,5 +23,9 @@ export const CUSTOMER_SERVICE_ROUTES: Routes = [
         component: ExceptionListComponent
       }
     ]
+  },
+  {
+    path: 'after-sales',
+    component: AfterSalesComponent
   }
 ];

+ 14 - 0
src/app/pages/customer-service/dashboard/dashboard.html

@@ -68,6 +68,20 @@
         </div>
       </div>
 
+      <div class="stat-card" (click)="handleAfterSalesClick()" title="点击查看售后服务详情">
+        <div class="stat-icon success">
+          <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor">
+            <path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"></path>
+          </svg>
+        </div>
+        <div class="stat-content">
+          <div class="stat-value">{{ stats.afterSalesCount() }}</div>
+          <div class="stat-label">售后服务</div>
+        </div>
+        <div class="stat-trend positive">
+          <span>+3</span>
+        </div>
+      </div>
 
     </div>
 

+ 5 - 0
src/app/pages/customer-service/dashboard/dashboard.ts

@@ -20,6 +20,7 @@ export class Dashboard implements OnInit, OnDestroy {
     newConsultations: signal(12),
     pendingAssignments: signal(5),
     exceptionProjects: signal(2),
+    afterSalesCount: signal(15), // 售后服务数量
     // 新增核心指标
     conversionRateToday: signal(36), // 当日成交率(%)
     pendingComplaints: signal(3),   // 待处理投诉数
@@ -560,6 +561,10 @@ export class Dashboard implements OnInit, OnDestroy {
     this.navigateToDetail('exceptions');
   }
 
+  handleAfterSalesClick(): void {
+    this.router.navigate(['/customer-service/after-sales']);
+  }
+
   // 导航到详情页
   private navigateToDetail(type: 'consultations' | 'assignments' | 'exceptions'): void {
     const routeMap = {

+ 774 - 0
src/app/pages/customer-service/dashboard/pages/after-sales/after-sales.component.html

@@ -0,0 +1,774 @@
+<div class="after-sales-container hr-page">
+  <!-- 概览视图 -->
+  @if (currentView() === 'overview') {
+    <header class="page-header">
+      <h1>售后服务</h1>
+      <p class="page-description">管理已完结项目的售后服务,包括尾款管理、客户评价处理和建议收集</p>
+    </header>
+
+    <!-- 统计数据看板 -->
+    <div class="stats-dashboard">
+      <div class="stat-card">
+        <div class="stat-icon total">
+          <mat-icon>assignment</mat-icon>
+        </div>
+        <div class="stat-content">
+          <div class="stat-value">{{ stats().total }}</div>
+          <div class="stat-label">总项目数</div>
+        </div>
+      </div>
+
+      <div class="stat-card">
+        <div class="stat-icon pending">
+          <mat-icon>payment</mat-icon>
+        </div>
+        <div class="stat-content">
+          <div class="stat-value">{{ stats().pendingPayment }}</div>
+          <div class="stat-label">待收尾款</div>
+        </div>
+      </div>
+
+      <div class="stat-card">
+        <div class="stat-icon overdue">
+          <mat-icon>schedule</mat-icon>
+        </div>
+        <div class="stat-content">
+          <div class="stat-value">{{ stats().overduePayment }}</div>
+          <div class="stat-label">逾期尾款</div>
+        </div>
+      </div>
+
+      <div class="stat-card">
+        <div class="stat-icon review">
+          <mat-icon>rate_review</mat-icon>
+        </div>
+        <div class="stat-content">
+          <div class="stat-value">{{ stats().pendingReview }}</div>
+          <div class="stat-label">待处理评价</div>
+        </div>
+      </div>
+
+      <div class="stat-card">
+        <div class="stat-icon priority">
+          <mat-icon>priority_high</mat-icon>
+        </div>
+        <div class="stat-content">
+          <div class="stat-value">{{ stats().highPriority }}</div>
+          <div class="stat-label">高优先级</div>
+        </div>
+      </div>
+    </div>
+
+    <!-- 搜索和筛选栏 -->
+    <div class="filter-bar">
+      <div class="search-section">
+        <mat-form-field appearance="outline" class="search-field ios-select">
+          <mat-label>搜索项目</mat-label>
+          <input matInput 
+                 placeholder="项目编号、项目名称或客户名称" 
+                 [(ngModel)]="searchTerm"
+                 (ngModelChange)="searchTerm.set($event)">
+          <mat-icon matSuffix>search</mat-icon>
+        </mat-form-field>
+      </div>
+
+      <div class="filter-section">
+        <mat-form-field appearance="outline" class="filter-field ios-select">
+          <mat-label>尾款状态</mat-label>
+          <mat-select [(ngModel)]="selectedPaymentStatus" 
+                      (ngModelChange)="selectedPaymentStatus.set($event)">
+            <mat-option value="all">
+              <div class="option-content">
+                <mat-icon>payment</mat-icon>
+                <span>全部</span>
+              </div>
+            </mat-option>
+            <mat-option value="pending">
+              <div class="option-content">
+                <mat-icon>schedule</mat-icon>
+                <span>待收款</span>
+              </div>
+            </mat-option>
+            <mat-option value="partial">
+              <div class="option-content">
+                <mat-icon>partial_fulfillment</mat-icon>
+                <span>部分收款</span>
+              </div>
+            </mat-option>
+            <mat-option value="completed">
+              <div class="option-content">
+                <mat-icon>check_circle</mat-icon>
+                <span>已完成</span>
+              </div>
+            </mat-option>
+            <mat-option value="overdue">
+              <div class="option-content">
+                <mat-icon>warning</mat-icon>
+                <span>逾期</span>
+              </div>
+            </mat-option>
+          </mat-select>
+        </mat-form-field>
+
+        <mat-form-field appearance="outline" class="filter-field ios-select">
+          <mat-label>评价状态</mat-label>
+          <mat-select [(ngModel)]="selectedReviewStatus" 
+                      (ngModelChange)="selectedReviewStatus.set($event)">
+            <mat-option value="all">
+              <div class="option-content">
+                <mat-icon>star</mat-icon>
+                <span>全部</span>
+              </div>
+            </mat-option>
+            <mat-option value="pending">
+              <div class="option-content">
+                <mat-icon>star_border</mat-icon>
+                <span>待评价</span>
+              </div>
+            </mat-option>
+            <mat-option value="positive">
+              <div class="option-content">
+                <mat-icon>thumb_up</mat-icon>
+                <span>好评</span>
+              </div>
+            </mat-option>
+            <mat-option value="negative">
+              <div class="option-content">
+                <mat-icon>thumb_down</mat-icon>
+                <span>差评</span>
+              </div>
+            </mat-option>
+            <mat-option value="no-review">
+              <div class="option-content">
+                <mat-icon>help_outline</mat-icon>
+                <span>无评价</span>
+              </div>
+            </mat-option>
+          </mat-select>
+        </mat-form-field>
+
+        <mat-form-field appearance="outline" class="filter-field ios-select">
+          <mat-label>优先级</mat-label>
+          <mat-select [(ngModel)]="selectedPriority" 
+                      (ngModelChange)="selectedPriority.set($event)">
+            <mat-option value="all">
+              <div class="option-content">
+                <mat-icon>priority_high</mat-icon>
+                <span>全部</span>
+              </div>
+            </mat-option>
+            <mat-option value="high">
+              <div class="option-content">
+                <mat-icon>keyboard_double_arrow_up</mat-icon>
+                <span>高</span>
+              </div>
+            </mat-option>
+            <mat-option value="medium">
+              <div class="option-content">
+                <mat-icon>keyboard_arrow_up</mat-icon>
+                <span>中</span>
+              </div>
+            </mat-option>
+            <mat-option value="low">
+              <div class="option-content">
+                <mat-icon>keyboard_arrow_down</mat-icon>
+                <span>低</span>
+              </div>
+            </mat-option>
+          </mat-select>
+        </mat-form-field>
+      </div>
+    </div>
+
+    <!-- 项目列表 -->
+    <div class="projects-list">
+      @for (project of filteredProjects(); track project.id) {
+        <div class="project-card" (click)="selectProject(project)">
+          <div class="project-header">
+            <div class="project-info">
+              <h3 class="project-name">{{ project.projectName }}</h3>
+              <div class="project-meta">
+                <span class="project-code">{{ project.projectCode }}</span>
+                <span class="customer-name">{{ project.customerName }}</span>
+                <span class="completion-date">完成于 {{ formatDate(project.completedDate) }}</span>
+              </div>
+            </div>
+            <div class="priority-badge" [class]="getPriorityClass(project.priority)">
+              {{ getPriorityText(project.priority) }}优先级
+            </div>
+          </div>
+
+          <div class="project-content">
+            <!-- 尾款状态 -->
+            <div class="status-section">
+              <div class="status-item">
+                <div class="status-label">尾款状态</div>
+                <div class="status-value">
+                  <span class="status-badge" [class]="getPaymentStatusClass(project.finalPaymentStatus)">
+                    {{ getPaymentStatusText(project.finalPaymentStatus) }}
+                  </span>
+                </div>
+              </div>
+
+              <div class="payment-progress">
+                <div class="progress-info">
+                  <span>{{ formatAmount(project.paidAmount) }} / {{ formatAmount(project.finalPaymentAmount) }}</span>
+                  <span class="progress-percent">{{ getPaymentProgress(project) }}%</span>
+                </div>
+                <div class="progress-bar">
+                  <div class="progress-fill" [style.width.%]="getPaymentProgress(project)"></div>
+                </div>
+              </div>
+            </div>
+
+            <!-- 评价状态 -->
+            <div class="status-section">
+              <div class="status-item">
+                <div class="status-label">客户评价</div>
+                <div class="status-value">
+                  <span class="status-badge" [class]="getReviewStatusClass(project.reviewStatus)">
+                    {{ getReviewStatusText(project.reviewStatus) }}
+                  </span>
+                  @if (project.reviewScore) {
+                    <div class="review-score">
+                      @for (star of [1,2,3,4,5]; track star) {
+                        <mat-icon [class.filled]="star <= project.reviewScore!">star</mat-icon>
+                      }
+                    </div>
+                  }
+                </div>
+              </div>
+            </div>
+
+            <!-- 建议数量 -->
+            <div class="status-section">
+              <div class="status-item">
+                <div class="status-label">客户建议</div>
+                <div class="status-value">
+                  <span class="suggestion-count">{{ project.suggestionCount }} 条</span>
+                </div>
+              </div>
+            </div>
+          </div>
+
+          <div class="project-footer">
+            <div class="last-contact">
+              最后联系:{{ formatDate(project.lastContactDate) }}
+            </div>
+            <div class="action-hint">
+              <mat-icon>arrow_forward</mat-icon>
+              点击查看详情
+            </div>
+          </div>
+        </div>
+      } @empty {
+        <div class="empty-state">
+          <mat-icon>inbox</mat-icon>
+          <h3>暂无售后项目</h3>
+          <p>当前筛选条件下没有找到相关项目</p>
+        </div>
+      }
+    </div>
+  }
+
+  <!-- 详情视图 -->
+  @if (currentView() === 'detail' && selectedProject()) {
+    <div class="detail-view">
+      <div class="detail-header">
+        <button class="back-button" (click)="backToOverview()">
+          <mat-icon>arrow_back</mat-icon>
+          返回概览
+        </button>
+        <div class="project-info">
+          <h2>{{ selectedProject()?.projectName }}</h2>
+          <span class="project-code">{{ selectedProject()?.projectCode }}</span>
+          <span class="customer-name">客户:{{ selectedProject()?.customerName }}</span>
+        </div>
+      </div>
+      
+      <!-- 功能模块标签 -->
+      <div class="detail-tabs">
+        <button 
+          class="tab-button"
+          [class.active]="activeDetailTab() === 'payment'"
+          (click)="switchDetailTab('payment')">
+          <mat-icon>payment</mat-icon>
+          尾款管理
+        </button>
+        <button 
+          class="tab-button"
+          [class.active]="activeDetailTab() === 'review'"
+          (click)="switchDetailTab('review')">
+          <mat-icon>star</mat-icon>
+          客户评价处理
+        </button>
+        <button 
+          class="tab-button"
+          [class.active]="activeDetailTab() === 'suggestion'"
+          (click)="switchDetailTab('suggestion')">
+          <mat-icon>feedback</mat-icon>
+          客户建议收集与处理
+        </button>
+      </div>
+      
+      <!-- 尾款管理模块 -->
+      @if (activeDetailTab() === 'payment') {
+        <div class="payment-management">
+          <div class="module-header">
+            <h3>尾款管理</h3>
+            <button class="upload-button" (click)="fileInput.click()" [disabled]="isUploadingFile()">
+              <mat-icon>upload</mat-icon>
+              {{ isUploadingFile() ? '上传中...' : '上传支付凭证' }}
+            </button>
+            <input #fileInput type="file" hidden multiple accept=".jpg,.jpeg,.png,.pdf" 
+                   (change)="handleFileUpload($event)">
+          </div>
+          
+          <div class="payment-summary">
+            <div class="summary-item">
+              <span class="label">应收尾款:</span>
+              <span class="amount">{{ formatCurrency(selectedProject()?.finalPaymentAmount || 0) }}</span>
+            </div>
+            <div class="summary-item">
+              <span class="label">已收金额:</span>
+              <span class="amount paid">{{ formatCurrency(selectedProject()?.paidAmount || 0) }}</span>
+            </div>
+            <div class="summary-item">
+              <span class="label">待收金额:</span>
+              <span class="amount pending">{{ formatCurrency((selectedProject()?.finalPaymentAmount || 0) - (selectedProject()?.paidAmount || 0)) }}</span>
+            </div>
+          </div>
+          
+          @if (selectedProject()?.paymentRecords && selectedProject()?.paymentRecords!.length > 0) {
+            <div class="payment-records">
+              <h4>支付记录</h4>
+              @for (record of selectedProject()?.paymentRecords; track record.id) {
+                <div class="payment-record">
+                  <div class="record-header">
+                    <span class="amount">{{ formatCurrency(record.amount) }}</span>
+                    <span class="status" [class]="'status-' + record.status">
+                      {{ getPaymentRecordStatusText(record.status) }}
+                    </span>
+                    <span class="date">{{ formatDate(record.paymentDate) }}</span>
+                  </div>
+                  <div class="record-details">
+                    <p><strong>支付方式:</strong>{{ record.paymentMethod }}</p>
+                    <p><strong>操作人:</strong>{{ record.operator }}</p>
+                    @if (record.remark) {
+                      <p><strong>备注:</strong>{{ record.remark }}</p>
+                    }
+                    @if (record.voucher) {
+                      <div class="voucher-preview">
+                        <strong>支付凭证:</strong>
+                        @if (record.voucher.previewUrl) {
+                          <img [src]="record.voucher.previewUrl" [alt]="record.voucher.fileName" class="voucher-image">
+                        } @else {
+                          <a [href]="record.voucher.fileUrl" target="_blank" class="voucher-link">
+                            <mat-icon>description</mat-icon>
+                            {{ record.voucher.fileName }}
+                          </a>
+                        }
+                      </div>
+                    }
+                  </div>
+                  
+                  @if (record.statusHistory.length > 0) {
+                    <div class="status-history">
+                      <h5>状态变更记录</h5>
+                      @for (history of record.statusHistory; track history.id) {
+                        <div class="history-item">
+                          <span class="history-date">{{ formatDate(history.changeDate) }}</span>
+                          <span class="history-operator">{{ history.operator }}</span>
+                          <span class="history-status">{{ history.status }}</span>
+                          <span class="history-reason">{{ history.reason }}</span>
+                        </div>
+                      }
+                    </div>
+                  }
+                </div>
+              }
+            </div>
+          } @else {
+            <div class="empty-state">
+              <mat-icon>payment</mat-icon>
+              <p>暂无支付记录</p>
+            </div>
+          }
+        </div>
+      }
+      
+      <!-- 客户评价处理模块 -->
+      @if (activeDetailTab() === 'review') {
+        <div class="review-management">
+          <div class="module-header">
+            <h3>客户评价处理</h3>
+            <button class="add-review-button" (click)="showAddReviewForm = !showAddReviewForm">
+              <mat-icon>add</mat-icon>
+              录入评价
+            </button>
+          </div>
+          
+          @if (showAddReviewForm) {
+            <div class="add-review-form">
+              <div class="form-header">
+                <div class="header-content">
+                  <mat-icon class="header-icon">rate_review</mat-icon>
+                  <h4>录入客户评价</h4>
+                </div>
+                <button type="button" mat-icon-button class="close-button" (click)="showAddReviewForm = false">
+                  <mat-icon>close</mat-icon>
+                </button>
+              </div>
+              
+              <form #reviewForm="ngForm" (ngSubmit)="addReviewRecord(reviewForm.value)" class="review-form">
+                <div class="form-grid">
+                  <div class="form-row">
+                    <mat-form-field class="ios-select" appearance="outline">
+                      <mat-label>反馈渠道</mat-label>
+                      <mat-select name="reviewChannel" ngModel required>
+                        <mat-option value="phone">
+                          <div class="option-content">
+                            <mat-icon>phone</mat-icon>
+                            <span>电话回访</span>
+                          </div>
+                        </mat-option>
+                        <mat-option value="wechat">
+                          <div class="option-content">
+                            <mat-icon>chat</mat-icon>
+                            <span>微信沟通</span>
+                          </div>
+                        </mat-option>
+                        <mat-option value="email">
+                          <div class="option-content">
+                            <mat-icon>email</mat-icon>
+                            <span>邮件反馈</span>
+                          </div>
+                        </mat-option>
+                        <mat-option value="face-to-face">
+                          <div class="option-content">
+                            <mat-icon>person</mat-icon>
+                            <span>面对面交流</span>
+                          </div>
+                        </mat-option>
+                        <mat-option value="other">
+                          <div class="option-content">
+                            <mat-icon>more_horiz</mat-icon>
+                            <span>其他渠道</span>
+                          </div>
+                        </mat-option>
+                      </mat-select>
+                    </mat-form-field>
+                    
+                    <mat-form-field class="ios-select" appearance="outline">
+                      <mat-label>评价等级</mat-label>
+                      <mat-select name="rating" ngModel required>
+                        <mat-option value="5">
+                          <div class="option-content rating-option">
+                            <div class="stars">
+                              @for (star of [1,2,3,4,5]; track star) {
+                                <span class="star-icon filled">★</span>
+                              }
+                            </div>
+                            <span>非常满意</span>
+                          </div>
+                        </mat-option>
+                        <mat-option value="4">
+                          <div class="option-content rating-option">
+                            <div class="stars">
+                              @for (star of [1,2,3,4]; track star) {
+                                <span class="star-icon filled">★</span>
+                              }
+                              <span class="star-icon">☆</span>
+                            </div>
+                            <span>满意</span>
+                          </div>
+                        </mat-option>
+                        <mat-option value="3">
+                          <div class="option-content rating-option">
+                            <div class="stars">
+                              @for (star of [1,2,3]; track star) {
+                                <span class="star-icon filled">★</span>
+                              }
+                              @for (star of [4,5]; track star) {
+                                <span class="star-icon">☆</span>
+                              }
+                            </div>
+                            <span>一般</span>
+                          </div>
+                        </mat-option>
+                        <mat-option value="2">
+                          <div class="option-content rating-option">
+                            <div class="stars">
+                              @for (star of [1,2]; track star) {
+                                <span class="star-icon filled">★</span>
+                              }
+                              @for (star of [3,4,5]; track star) {
+                                <span class="star-icon">☆</span>
+                              }
+                            </div>
+                            <span>不满意</span>
+                          </div>
+                        </mat-option>
+                        <mat-option value="1">
+                          <div class="option-content rating-option">
+                            <span class="star-icon filled">★</span>
+                            @for (star of [2,3,4,5]; track star) {
+                              <span class="star-icon">☆</span>
+                            }
+                            <span>非常不满意</span>
+                          </div>
+                        </mat-option>
+                      </mat-select>
+                    </mat-form-field>
+                  </div>
+                  
+                  <mat-form-field class="full-width ios-select" appearance="outline">
+                    <mat-label>评价内容</mat-label>
+                    <textarea matInput rows="4" name="content" ngModel required 
+                              placeholder="请详细描述客户的评价内容,包括满意的方面和需要改进的地方"></textarea>
+                    <mat-hint>请尽量详细记录客户的反馈意见</mat-hint>
+                  </mat-form-field>
+                  
+                  <mat-form-field class="full-width ios-select" appearance="outline">
+                    <mat-label>处理建议</mat-label>
+                    <textarea matInput rows="2" name="remark" ngModel 
+                              placeholder="基于客户评价,提出相应的改进建议或后续处理方案"></textarea>
+                    <mat-hint>可选:添加针对性的改进建议</mat-hint>
+                  </mat-form-field>
+                </div>
+                
+                <div class="form-actions">
+                  <button type="button" mat-stroked-button (click)="showAddReviewForm = false">
+                    <mat-icon>close</mat-icon>
+                    取消
+                  </button>
+                  <button type="submit" mat-raised-button color="primary" [disabled]="isSubmittingForm()">
+                    <mat-icon>{{ isSubmittingForm() ? 'sync' : 'save' }}</mat-icon>
+                    {{ isSubmittingForm() ? '提交中...' : '确认录入' }}
+                  </button>
+                </div>
+              </form>
+            </div>
+          }
+          
+          @if (selectedProject()?.reviewRecords && selectedProject()?.reviewRecords!.length > 0) {
+            <div class="review-records">
+              <h4>评价记录</h4>
+              @for (review of selectedProject()?.reviewRecords; track review.id) {
+                <div class="review-record">
+                  <div class="review-header">
+                    <span class="review-type">{{ review.reviewType === 'system' ? '系统评价' : '手动录入' }}</span>
+                    <span class="review-channel">{{ review.reviewChannel }}</span>
+                    <span class="review-date">{{ formatDate(review.reviewDate) }}</span>
+                    @if (review.score) {
+                      <div class="review-score">
+                        @for (star of [1,2,3,4,5]; track star) {
+                          <mat-icon [class.filled]="star <= review.score!">star</mat-icon>
+                        }
+                      </div>
+                    }
+                  </div>
+                  <div class="review-content">
+                    <p>{{ review.content }}</p>
+                    @if (review.remark) {
+                      <p class="review-remark"><strong>备注:</strong>{{ review.remark }}</p>
+                    }
+                    @if (review.operator) {
+                      <p class="review-operator"><strong>录入人:</strong>{{ review.operator }}</p>
+                    }
+                  </div>
+                  @if (review.followUpReminder) {
+                    <div class="follow-up-reminder">
+                      <mat-icon>schedule</mat-icon>
+                      <span>跟进提醒:{{ formatDate(review.followUpReminder) }}</span>
+                    </div>
+                  }
+                </div>
+              }
+            </div>
+          } @else {
+            <div class="empty-state">
+              <mat-icon>star_border</mat-icon>
+              <p>暂无评价记录</p>
+            </div>
+          }
+        </div>
+      }
+      
+      <!-- 客户建议收集与处理模块 -->
+      @if (activeDetailTab() === 'suggestion') {
+        <div class="suggestion-management">
+          <div class="module-header">
+            <h3>客户建议收集与处理</h3>
+          </div>
+          
+          @if (selectedProject()?.suggestions && selectedProject()?.suggestions!.length > 0) {
+            <div class="suggestion-records">
+              @for (suggestion of selectedProject()?.suggestions; track suggestion.id) {
+                <div class="suggestion-record">
+                  <div class="suggestion-header">
+                    <span class="suggestion-category">{{ getSuggestionCategoryLabel(suggestion.category) }}</span>
+                    <span class="suggestion-status" [class]="'status-' + suggestion.status">
+                      {{ getSuggestionStatusText(suggestion.status) }}
+                    </span>
+                    <span class="suggestion-priority" [class]="'priority-' + suggestion.priority">
+                      {{ suggestion.priority === 'high' ? '高优先级' : suggestion.priority === 'medium' ? '中优先级' : '低优先级' }}
+                    </span>
+                    <span class="suggestion-date">{{ formatDate(suggestion.submitDate) }}</span>
+                  </div>
+                  
+                  <div class="suggestion-content">
+                    <p><strong>建议内容:</strong>{{ suggestion.content }}</p>
+                    @if (suggestion.assignee) {
+                      <p><strong>处理人:</strong>{{ suggestion.assignee }}</p>
+                    }
+                    @if (suggestion.response) {
+                      <div class="suggestion-response">
+                        <p><strong>回复内容:</strong>{{ suggestion.response }}</p>
+                        @if (suggestion.responseDate) {
+                          <p><strong>回复时间:</strong>{{ formatDate(suggestion.responseDate) }}</p>
+                        }
+                      </div>
+                    }
+                  </div>
+                  
+                  <div class="suggestion-actions">
+                    @if (suggestion.status !== 'completed' && suggestion.status !== 'rejected') {
+                      <button mat-button (click)="showUpdateForm[suggestion.id] = !showUpdateForm[suggestion.id]">
+                        <mat-icon>edit</mat-icon>
+                        更新状态
+                      </button>
+                    }
+                  </div>
+                  
+                  @if (showUpdateForm[suggestion.id]) {
+                    <div class="update-form">
+                      <div class="update-form-header">
+                        <div class="header-content">
+                          <mat-icon class="header-icon">edit_note</mat-icon>
+                          <h5>更新处理状态</h5>
+                        </div>
+                        <button type="button" mat-icon-button class="close-button" (click)="showUpdateForm[suggestion.id] = false">
+                          <mat-icon>close</mat-icon>
+                        </button>
+                      </div>
+                      
+                      <form #updateForm="ngForm" (ngSubmit)="updateSuggestionStatus(suggestion.id, updateForm.value.status, updateForm.value.response)" class="status-update-form">
+                        <div class="form-grid">
+                          <div class="form-row">
+                            <mat-form-field class="ios-select status-field" appearance="outline">
+                              <mat-label>处理状态</mat-label>
+                              <mat-select name="status" [ngModel]="suggestion.status" required>
+                                <mat-option value="pending">
+                                  <div class="option-content status-option">
+                                    <mat-icon class="status-icon pending">schedule</mat-icon>
+                                    <div class="status-info">
+                                      <span class="status-name">待处理</span>
+                                      <small class="status-desc">等待分配处理人员</small>
+                                    </div>
+                                  </div>
+                                </mat-option>
+                                <mat-option value="processing">
+                                  <div class="option-content status-option">
+                                    <mat-icon class="status-icon processing">sync</mat-icon>
+                                    <div class="status-info">
+                                      <span class="status-name">处理中</span>
+                                      <small class="status-desc">正在积极处理中</small>
+                                    </div>
+                                  </div>
+                                </mat-option>
+                                <mat-option value="completed">
+                                  <div class="option-content status-option">
+                                    <mat-icon class="status-icon completed">check_circle</mat-icon>
+                                    <div class="status-info">
+                                      <span class="status-name">已完成</span>
+                                      <small class="status-desc">建议已采纳并实施</small>
+                                    </div>
+                                  </div>
+                                </mat-option>
+                                <mat-option value="rejected">
+                                  <div class="option-content status-option">
+                                    <mat-icon class="status-icon rejected">cancel</mat-icon>
+                                    <div class="status-info">
+                                      <span class="status-name">已拒绝</span>
+                                      <small class="status-desc">建议不予采纳</small>
+                                    </div>
+                                  </div>
+                                </mat-option>
+                              </mat-select>
+                            </mat-form-field>
+                            
+                            <mat-form-field class="assignee-field ios-select" appearance="outline">
+              <mat-label>处理人员</mat-label>
+              <mat-select name="assignee" [ngModel]="suggestion.assignee">
+                <mat-option value="张三">
+                  <div class="option-content">
+                    <mat-icon>person</mat-icon>
+                    <span>张三 - 客服主管</span>
+                  </div>
+                </mat-option>
+                <mat-option value="李四">
+                  <div class="option-content">
+                    <mat-icon>person</mat-icon>
+                    <span>李四 - 技术支持</span>
+                  </div>
+                </mat-option>
+                <mat-option value="王五">
+                  <div class="option-content">
+                    <mat-icon>person</mat-icon>
+                    <span>王五 - 产品经理</span>
+                  </div>
+                </mat-option>
+              </mat-select>
+            </mat-form-field>
+                          </div>
+                          
+                          <mat-form-field class="full-width" appearance="outline">
+                            <mat-label>处理回复</mat-label>
+                            <textarea matInput rows="4" name="response" [ngModel]="suggestion.response" 
+                                      placeholder="请详细说明处理结果、采取的措施或拒绝原因"></textarea>
+                            <mat-hint>请提供详细的处理说明,便于后续跟踪</mat-hint>
+                          </mat-form-field>
+                        </div>
+                        
+                        <div class="form-actions">
+                          <button type="button" mat-stroked-button (click)="showUpdateForm[suggestion.id] = false">
+                            <mat-icon>close</mat-icon>
+                            取消
+                          </button>
+                          <button type="submit" mat-raised-button color="primary" [disabled]="isSubmittingForm()">
+                            <mat-icon>{{ isSubmittingForm() ? 'sync' : 'update' }}</mat-icon>
+                            {{ isSubmittingForm() ? '更新中...' : '确认更新' }}
+                          </button>
+                        </div>
+                      </form>
+                    </div>
+                  }
+                  
+                  @if (suggestion.statusHistory.length > 0) {
+                    <div class="status-history">
+                      <h5>处理记录</h5>
+                      @for (history of suggestion.statusHistory; track history.id) {
+                        <div class="history-item">
+                          <span class="history-date">{{ formatDate(history.changeDate) }}</span>
+                          <span class="history-operator">{{ history.operator }}</span>
+                          <span class="history-status">{{ history.status }}</span>
+                          <span class="history-remark">{{ history.remark }}</span>
+                        </div>
+                      }
+                    </div>
+                  }
+                </div>
+              }
+            </div>
+          } @else {
+            <div class="empty-state">
+              <mat-icon>feedback</mat-icon>
+              <p>暂无客户建议</p>
+            </div>
+          }
+        </div>
+      }
+    </div>
+  }
+</div>

+ 2012 - 0
src/app/pages/customer-service/dashboard/pages/after-sales/after-sales.component.scss

@@ -0,0 +1,2012 @@
+// 引入iOS主题样式
+@use '../../../../../shared/styles/ios-theme' as ios;
+@use '../../../../../shared/styles/variables';
+
+// 参考HR考勤页面的设计风格,使用iOS主题变量
+$primary-color: ios.$ios-primary;
+$primary-light: #3b82f6;
+$secondary-color: #0d9488;
+$success-color: ios.$ios-success;
+$warning-color: ios.$ios-warning;
+$error-color: ios.$ios-error;
+$info-color: ios.$ios-info;
+$text-primary: ios.$ios-text-primary;
+$text-secondary: ios.$ios-text-secondary;
+$text-tertiary: ios.$ios-text-tertiary;
+$bg-primary: ios.$ios-background;
+$bg-secondary: ios.$ios-background-secondary;
+$bg-tertiary: ios.$ios-background-tertiary;
+$border-color: ios.$ios-border;
+$shadow-sm: ios.$ios-shadow-sm;
+$shadow-md: ios.$ios-shadow-md;
+$shadow-lg: ios.$ios-shadow-lg;
+$border-radius: ios.$ios-radius-md;
+$transition: all 0.2s ease;
+
+// 主容器样式
+.after-sales-container {
+  padding: 24px;
+  min-height: 100vh;
+  background-color: $bg-secondary;
+  font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
+}
+
+// 页面标题
+.page-header {
+  margin-bottom: 24px;
+  padding-bottom: 16px;
+  border-bottom: 2px solid $border-color;
+
+  h1 {
+    font-size: 28px;
+    font-weight: 700;
+    color: $text-primary;
+    margin: 0 0 8px 0;
+  }
+
+  .page-description {
+    font-size: 16px;
+    color: $text-secondary;
+    margin: 0;
+  }
+}
+
+// 统计数据看板
+.stats-dashboard {
+  display: grid;
+  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
+  gap: 16px;
+  margin-bottom: 24px;
+
+  .stat-card {
+    background-color: $bg-primary;
+    border-radius: $border-radius;
+    padding: 20px;
+    box-shadow: $shadow-sm;
+    display: flex;
+    align-items: center;
+    gap: 16px;
+    transition: $transition;
+
+    &:hover {
+      box-shadow: $shadow-md;
+      transform: translateY(-2px);
+    }
+
+    .stat-icon {
+      width: 48px;
+      height: 48px;
+      border-radius: 50%;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      color: white;
+
+      &.total {
+        background-color: $primary-color;
+      }
+
+      &.pending {
+        background-color: $warning-color;
+      }
+
+      &.overdue {
+        background-color: $error-color;
+      }
+
+      &.review {
+        background-color: $secondary-color;
+      }
+
+      &.priority {
+        background-color: $info-color;
+      }
+
+      mat-icon {
+        font-size: 24px;
+        width: 24px;
+        height: 24px;
+      }
+    }
+
+    .stat-content {
+      flex: 1;
+
+      .stat-value {
+        font-size: 24px;
+        font-weight: 700;
+        color: $text-primary;
+        margin-bottom: 4px;
+      }
+
+      .stat-label {
+        font-size: 14px;
+        color: $text-secondary;
+      }
+    }
+  }
+}
+
+// 搜索和筛选栏
+.filter-bar {
+  background-color: $bg-primary;
+  border-radius: $border-radius;
+  padding: 20px;
+  margin-bottom: 24px;
+  box-shadow: $shadow-sm;
+
+  .search-section {
+    margin-bottom: ios.$ios-spacing-md;
+
+    .search-field {
+      width: 100%;
+      max-width: 400px;
+      
+      .mat-mdc-form-field {
+        border-radius: ios.$ios-radius-md;
+        font-family: ios.$ios-font-family;
+        
+        .mat-mdc-text-field-wrapper {
+          background: white;
+          border: 1px solid ios.$ios-border;
+          border-radius: ios.$ios-radius-md;
+          transition: all 0.3s ease;
+
+          &:hover {
+            border-color: ios.$ios-primary;
+          }
+        }
+
+        .mat-mdc-form-field-infix {
+          color: ios.$ios-text-primary;
+          font-family: ios.$ios-font-family;
+          padding: ios.$ios-spacing-sm;
+        }
+
+        &.mat-focused .mat-mdc-text-field-wrapper {
+          border-color: ios.$ios-primary;
+          box-shadow: 0 0 0 2px rgba(0, 122, 255, 0.1);
+        }
+      }
+    }
+  }
+
+  .filter-section {
+    display: flex;
+    gap: ios.$ios-spacing-md;
+    flex-wrap: wrap;
+
+    .filter-field {
+      min-width: 150px;
+      
+      .mat-mdc-form-field {
+        border-radius: ios.$ios-radius-md;
+        font-family: ios.$ios-font-family;
+        
+        .mat-mdc-text-field-wrapper {
+          background: white;
+          border: 1px solid ios.$ios-border;
+          border-radius: ios.$ios-radius-md;
+          transition: all 0.3s ease;
+
+          &:hover {
+            border-color: ios.$ios-primary;
+          }
+        }
+
+        .mat-mdc-form-field-infix {
+          color: ios.$ios-text-primary;
+          font-family: ios.$ios-font-family;
+          padding: ios.$ios-spacing-sm;
+        }
+
+        // 选择框样式
+        .mat-mdc-select-value {
+          color: ios.$ios-text-primary;
+          font-family: ios.$ios-font-family;
+          padding: ios.$ios-spacing-sm 0;
+        }
+
+        .mat-mdc-select-arrow {
+          color: ios.$ios-text-secondary;
+          transition: transform 0.3s ease;
+        }
+
+        &.mat-focused {
+          .mat-mdc-text-field-wrapper {
+            border-color: ios.$ios-primary;
+            box-shadow: 0 0 0 2px rgba(0, 122, 255, 0.1);
+          }
+          
+          .mat-mdc-select-arrow {
+            color: ios.$ios-primary;
+          }
+        }
+      }
+    }
+  }
+}
+
+// 项目列表
+.projects-list {
+  display: flex;
+  flex-direction: column;
+  gap: 16px;
+
+  .project-card {
+    background-color: $bg-primary;
+    border-radius: $border-radius;
+    padding: 24px;
+    box-shadow: $shadow-sm;
+    cursor: pointer;
+    transition: $transition;
+    border: 2px solid transparent;
+
+    &:hover {
+      box-shadow: $shadow-md;
+      transform: translateY(-2px);
+      border-color: $primary-light;
+    }
+
+    .project-header {
+      display: flex;
+      justify-content: space-between;
+      align-items: flex-start;
+      margin-bottom: 16px;
+
+      .project-info {
+        flex: 1;
+
+        .project-name {
+          font-size: 18px;
+          font-weight: 600;
+          color: $text-primary;
+          margin: 0 0 8px 0;
+        }
+
+        .project-meta {
+          display: flex;
+          gap: 16px;
+          flex-wrap: wrap;
+
+          span {
+            font-size: 14px;
+            color: $text-secondary;
+
+            &.project-code {
+              font-weight: 500;
+              color: $primary-color;
+            }
+          }
+        }
+      }
+
+      .priority-badge {
+        padding: 4px 12px;
+        border-radius: 20px;
+        font-size: 12px;
+        font-weight: 500;
+
+        &.priority-high {
+          background-color: rgba($error-color, 0.1);
+          color: $error-color;
+        }
+
+        &.priority-medium {
+          background-color: rgba($warning-color, 0.1);
+          color: $warning-color;
+        }
+
+        &.priority-low {
+          background-color: rgba($success-color, 0.1);
+          color: $success-color;
+        }
+      }
+    }
+
+    .project-content {
+      display: grid;
+      grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
+      gap: 20px;
+      margin-bottom: 16px;
+
+      .status-section {
+        .status-item {
+          display: flex;
+          justify-content: space-between;
+          align-items: center;
+          margin-bottom: 8px;
+
+          .status-label {
+            font-size: 14px;
+            color: $text-secondary;
+            font-weight: 500;
+          }
+
+          .status-value {
+            display: flex;
+            align-items: center;
+            gap: 8px;
+
+            .status-badge {
+              padding: 4px 8px;
+              border-radius: 4px;
+              font-size: 12px;
+              font-weight: 500;
+
+              &.status-pending {
+                background-color: rgba($warning-color, 0.1);
+                color: $warning-color;
+              }
+
+              &.status-partial {
+                background-color: rgba($info-color, 0.1);
+                color: $info-color;
+              }
+
+              &.status-completed {
+                background-color: rgba($success-color, 0.1);
+                color: $success-color;
+              }
+
+              &.status-overdue {
+                background-color: rgba($error-color, 0.1);
+                color: $error-color;
+              }
+
+              &.review-pending {
+                background-color: rgba($warning-color, 0.1);
+                color: $warning-color;
+              }
+
+              &.review-positive {
+                background-color: rgba($success-color, 0.1);
+                color: $success-color;
+              }
+
+              &.review-negative {
+                background-color: rgba($error-color, 0.1);
+                color: $error-color;
+              }
+
+              &.review-none {
+                background-color: rgba($text-tertiary, 0.1);
+                color: $text-tertiary;
+              }
+            }
+
+            .review-score {
+              display: flex;
+              gap: 2px;
+
+              mat-icon {
+                font-size: 16px;
+                width: 16px;
+                height: 16px;
+                color: $text-tertiary;
+
+                &.filled {
+                  color: $warning-color;
+                }
+              }
+            }
+
+            .suggestion-count {
+              font-size: 14px;
+              font-weight: 500;
+              color: $text-primary;
+            }
+          }
+        }
+
+        .payment-progress {
+          margin-top: 8px;
+
+          .progress-info {
+            display: flex;
+            justify-content: space-between;
+            align-items: center;
+            margin-bottom: 4px;
+            font-size: 12px;
+            color: $text-secondary;
+
+            .progress-percent {
+              font-weight: 600;
+              color: $text-primary;
+            }
+          }
+
+          .progress-bar {
+            width: 100%;
+            height: 6px;
+            background-color: $bg-tertiary;
+            border-radius: 3px;
+            overflow: hidden;
+
+            .progress-fill {
+              height: 100%;
+              background-color: $success-color;
+              transition: width 0.3s ease;
+            }
+          }
+        }
+      }
+    }
+
+    .project-footer {
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      padding-top: 16px;
+      border-top: 1px solid $border-color;
+
+      .last-contact {
+        font-size: 14px;
+        color: $text-secondary;
+      }
+
+      .action-hint {
+        display: flex;
+        align-items: center;
+        gap: 4px;
+        font-size: 14px;
+        color: $primary-color;
+        font-weight: 500;
+
+        mat-icon {
+          font-size: 16px;
+          width: 16px;
+          height: 16px;
+        }
+      }
+    }
+  }
+
+  .empty-state {
+    text-align: center;
+    padding: 60px 20px;
+    background-color: $bg-primary;
+    border-radius: $border-radius;
+    box-shadow: $shadow-sm;
+
+    mat-icon {
+      font-size: 64px;
+      width: 64px;
+      height: 64px;
+      color: $text-tertiary;
+      margin-bottom: 16px;
+    }
+
+    h3 {
+      font-size: 18px;
+      font-weight: 600;
+      color: $text-primary;
+      margin: 0 0 8px 0;
+    }
+
+    p {
+      font-size: 14px;
+      color: $text-secondary;
+      margin: 0;
+    }
+  }
+}
+
+// 详情视图
+.detail-view {
+  .detail-header {
+    display: flex;
+    align-items: center;
+    gap: 16px;
+    margin-bottom: 24px;
+    padding-bottom: 16px;
+    border-bottom: 2px solid $border-color;
+
+    .back-button {
+      display: flex;
+      align-items: center;
+      gap: 8px;
+      padding: 8px 16px;
+      background: transparent;
+      border: 1px solid $border-color;
+      border-radius: $border-radius;
+      color: $text-secondary;
+      cursor: pointer;
+      transition: $transition;
+      font-size: 14px;
+
+      &:hover {
+        color: $primary-color;
+        background-color: $bg-tertiary;
+        border-color: $primary-color;
+      }
+
+      mat-icon {
+        font-size: 18px;
+        width: 18px;
+        height: 18px;
+      }
+    }
+
+    .project-info {
+      flex: 1;
+
+      h1 {
+        font-size: 24px;
+        font-weight: 700;
+        color: $text-primary;
+        margin: 0 0 8px 0;
+      }
+
+      .project-code,
+      .customer-name {
+        display: inline-block;
+        margin-right: 16px;
+        font-size: 14px;
+        color: $text-secondary;
+
+        &.project-code {
+          font-weight: 500;
+          color: $primary-color;
+        }
+      }
+    }
+  }
+
+  // 详情页面标签
+  .detail-tabs {
+    display: flex;
+    gap: 8px;
+    margin-bottom: 24px;
+    border-bottom: 1px solid $border-color;
+
+    .tab-button {
+      display: flex;
+      align-items: center;
+      gap: 8px;
+      padding: 12px 20px;
+      background: transparent;
+      border: none;
+      border-bottom: 2px solid transparent;
+      color: $text-secondary;
+      cursor: pointer;
+      transition: $transition;
+      font-size: 14px;
+      font-weight: 500;
+
+      &:hover {
+        color: $primary-color;
+        background-color: $bg-tertiary;
+      }
+
+      &.active {
+        color: $primary-color;
+        border-bottom-color: $primary-color;
+        background-color: $bg-tertiary;
+      }
+
+      mat-icon {
+        font-size: 18px;
+        width: 18px;
+        height: 18px;
+      }
+    }
+  }
+
+  // 模块通用样式
+  .module-header {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    margin-bottom: 20px;
+
+    h3 {
+      margin: 0;
+      color: $text-primary;
+      font-size: 18px;
+      font-weight: 600;
+    }
+
+    .upload-button,
+    .add-review-button {
+      display: flex;
+      align-items: center;
+      gap: 8px;
+      padding: 8px 16px;
+      background-color: $primary-color;
+      color: white;
+      border: none;
+      border-radius: $border-radius;
+      cursor: pointer;
+      transition: $transition;
+      font-size: 14px;
+
+      &:hover:not(:disabled) {
+        background-color: $primary-light;
+        transform: translateY(-1px);
+      }
+
+      &:disabled {
+        opacity: 0.6;
+        cursor: not-allowed;
+      }
+
+      mat-icon {
+        font-size: 16px;
+        width: 16px;
+        height: 16px;
+      }
+    }
+  }
+
+  // 尾款管理模块
+  .payment-management {
+    .payment-summary {
+      display: grid;
+      grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
+      gap: 16px;
+      margin-bottom: 24px;
+
+      .summary-item {
+        padding: 16px;
+        background-color: $bg-primary;
+        border: 1px solid $border-color;
+        border-radius: $border-radius;
+
+        .label {
+          display: block;
+          color: $text-secondary;
+          font-size: 12px;
+          margin-bottom: 8px;
+        }
+
+        .amount {
+          font-size: 18px;
+          font-weight: 600;
+          color: $text-primary;
+
+          &.paid {
+            color: $success-color;
+          }
+
+          &.pending {
+            color: $warning-color;
+          }
+        }
+      }
+    }
+
+    .payment-records {
+      h4 {
+        margin: 0 0 16px 0;
+        color: $text-primary;
+        font-size: 16px;
+        font-weight: 600;
+      }
+
+      .payment-record {
+        background-color: $bg-primary;
+        border: 1px solid $border-color;
+        border-radius: $border-radius;
+        padding: 16px;
+        margin-bottom: 16px;
+
+        .record-header {
+          display: flex;
+          align-items: center;
+          gap: 16px;
+          margin-bottom: 12px;
+
+          .amount {
+            font-size: 16px;
+            font-weight: 600;
+            color: $text-primary;
+          }
+
+          .status {
+            padding: 4px 8px;
+            border-radius: $border-radius;
+            font-size: 12px;
+            font-weight: 500;
+
+            &.status-pending {
+              background-color: rgba($warning-color, 0.1);
+              color: $warning-color;
+            }
+
+            &.status-verified {
+              background-color: rgba($success-color, 0.1);
+              color: $success-color;
+            }
+
+            &.status-rejected {
+              background-color: rgba($error-color, 0.1);
+              color: $error-color;
+            }
+          }
+
+          .date {
+            color: $text-secondary;
+            font-size: 14px;
+          }
+        }
+
+        .record-details {
+          margin-bottom: 12px;
+
+          p {
+            margin: 4px 0;
+            color: $text-primary;
+            font-size: 14px;
+          }
+
+          .voucher-preview {
+            margin-top: 8px;
+
+            .voucher-image {
+              max-width: 200px;
+              max-height: 150px;
+              border-radius: $border-radius;
+              border: 1px solid $border-color;
+              margin-left: 8px;
+            }
+
+            .voucher-link {
+              display: inline-flex;
+              align-items: center;
+              gap: 4px;
+              color: $primary-color;
+              text-decoration: none;
+              margin-left: 8px;
+
+              &:hover {
+                text-decoration: underline;
+              }
+            }
+          }
+        }
+
+        .status-history {
+          margin-top: ios.$ios-spacing-md;
+          background: ios.$ios-background-tertiary;
+          border-radius: ios.$ios-radius-md;
+          padding: ios.$ios-spacing-md;
+
+          h5 {
+            margin: 0 0 ios.$ios-spacing-sm 0;
+            color: ios.$ios-text-primary;
+            font-size: ios.$ios-font-size-headline;
+            font-weight: ios.$ios-font-weight-semibold;
+            font-family: ios.$ios-font-family;
+            display: flex;
+            align-items: center;
+            gap: ios.$ios-spacing-xs;
+
+            &::before {
+              content: '📋';
+              font-size: ios.$ios-font-size-body;
+            }
+          }
+
+          .history-item {
+            display: grid;
+            grid-template-columns: auto auto auto 1fr;
+            gap: ios.$ios-spacing-sm;
+            padding: ios.$ios-spacing-xs 0;
+            font-size: ios.$ios-font-size-caption-1;
+            color: ios.$ios-text-secondary;
+            font-family: ios.$ios-font-family;
+            border-bottom: 1px solid ios.$ios-border;
+
+            &:last-child {
+              border-bottom: none;
+            }
+
+            .history-date {
+              color: ios.$ios-text-primary;
+              font-weight: ios.$ios-font-weight-medium;
+            }
+          }
+        }
+      }
+    }
+  }
+
+  // 客户评价处理模块
+  .review-management {
+    .add-review-form {
+      background: linear-gradient(135deg, #ffffff 0%, #f8f9fa 100%);
+      border: 1px solid rgba(0, 122, 255, 0.1);
+      border-radius: 20px;
+      margin-bottom: 24px;
+      box-shadow: 
+        0 8px 32px rgba(0, 0, 0, 0.08),
+        0 2px 8px rgba(0, 0, 0, 0.04);
+      transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
+      position: relative;
+      overflow: hidden;
+
+      &::before {
+        content: '';
+        position: absolute;
+        top: 0;
+        left: 0;
+        right: 0;
+        height: 4px;
+        background: linear-gradient(90deg, #007aff, #5ac8fa, #34c759);
+        border-radius: 20px 20px 0 0;
+      }
+
+      &:hover {
+        transform: translateY(-2px);
+        box-shadow: 
+          0 12px 40px rgba(0, 0, 0, 0.12),
+          0 4px 16px rgba(0, 0, 0, 0.08);
+      }
+
+      .form-header {
+        display: flex;
+        align-items: center;
+        justify-content: space-between;
+        padding: 20px 24px 16px;
+        border-bottom: 1px solid rgba(0, 122, 255, 0.1);
+        margin-bottom: 20px;
+
+        .header-content {
+          display: flex;
+          align-items: center;
+          gap: 12px;
+
+          .header-icon {
+            color: ios.$ios-primary;
+            font-size: 24px;
+            width: 24px;
+            height: 24px;
+          }
+
+          h4 {
+            margin: 0;
+            color: #1d1d1f;
+            font-size: 20px;
+            font-weight: 700;
+            font-family: ios.$ios-font-family;
+          }
+        }
+
+        .close-button {
+          color: ios.$ios-text-secondary;
+          transition: all 0.2s ease;
+
+          &:hover {
+            color: ios.$ios-primary;
+            background: rgba(0, 122, 255, 0.1);
+          }
+        }
+      }
+
+      .review-form {
+        padding: 0 24px 24px;
+
+        .form-grid {
+          display: flex;
+          flex-direction: column;
+          gap: 20px;
+
+          .form-row {
+            display: grid;
+            grid-template-columns: 1fr 1fr;
+            gap: 16px;
+
+            @media (max-width: 768px) {
+              grid-template-columns: 1fr;
+            }
+          }
+        }
+      }
+
+      // 评价等级选项样式
+      .rating-option {
+        display: flex;
+        align-items: center;
+        gap: 12px;
+
+        .stars {
+          display: flex;
+          align-items: center;
+          gap: 2px;
+        }
+
+        .star-icon {
+          font-size: 16px;
+          line-height: 1;
+          display: inline-block;
+          user-select: none;
+
+          &.filled {
+            color: #FFD700;
+          }
+
+          &:not(.filled) {
+            color: #E0E0E0;
+          }
+        }
+      }
+
+      // 表单字段样式 - 采用人事板块的ios-select样式
+      .mat-mdc-form-field {
+        width: 100%;
+        border-radius: ios.$ios-radius-md;
+        font-family: ios.$ios-font-family;
+        margin-bottom: ios.$ios-spacing-sm;
+        
+        &.ios-select {
+          .mat-mdc-form-field-outline {
+            border-radius: 12px;
+            border-color: rgba(0, 122, 255, 0.3);
+          }
+
+          .mat-mdc-form-field-outline-thick {
+            border-color: #007AFF;
+            border-width: 2px;
+          }
+
+          .mat-mdc-select-trigger {
+            padding: 12px 16px;
+            font-size: 16px;
+            color: #333;
+            min-height: 24px;
+            display: flex;
+            align-items: center;
+          }
+
+          .mat-mdc-select-arrow {
+            color: #007AFF;
+            display: flex;
+            align-items: center;
+            justify-content: center;
+            transition: transform 0.3s ease;
+          }
+
+          .mat-mdc-select-value {
+            display: flex;
+            align-items: center;
+            min-height: 24px;
+            color: #333;
+            font-family: ios.$ios-font-family;
+            font-size: 16px;
+          }
+
+          &.mat-focused {
+            .mat-mdc-select-arrow {
+              color: #007AFF;
+              transform: rotate(180deg);
+            }
+          }
+        }
+        
+        .mat-mdc-text-field-wrapper {
+          background: white;
+          border: 1px solid ios.$ios-border;
+          border-radius: ios.$ios-radius-md;
+          transition: all 0.3s ease;
+
+          &:hover {
+            border-color: ios.$ios-primary;
+          }
+        }
+
+        .mat-mdc-form-field-infix {
+          color: ios.$ios-text-primary;
+          font-family: ios.$ios-font-family;
+          padding: ios.$ios-spacing-sm;
+        }
+
+        .mat-mdc-form-field-subscript-wrapper {
+          color: ios.$ios-text-secondary;
+          font-family: ios.$ios-font-family;
+          font-weight: ios.$ios-font-weight-medium;
+        }
+
+        .mat-mdc-form-field-error {
+          color: ios.$ios-error;
+          font-family: ios.$ios-font-family;
+          font-size: ios.$ios-font-size-caption-1;
+          margin-top: ios.$ios-spacing-xs;
+        }
+
+        // 输入框样式
+        .mat-mdc-input-element {
+          color: ios.$ios-text-primary;
+          font-family: ios.$ios-font-family;
+        }
+
+        &.mat-focused .mat-mdc-input-element {
+          color: ios.$ios-text-primary;
+        }
+      }
+
+      .form-actions {
+        display: flex;
+        gap: ios.$ios-spacing-sm;
+        justify-content: flex-end;
+        margin-top: ios.$ios-spacing-md;
+        padding-top: ios.$ios-spacing-md;
+        border-top: 1px solid ios.$ios-border;
+
+        .mat-mdc-button {
+          font-family: ios.$ios-font-family;
+          border-radius: ios.$ios-radius-md;
+          padding: ios.$ios-spacing-sm ios.$ios-spacing-lg;
+          font-weight: ios.$ios-font-weight-medium;
+
+          &.mat-primary {
+            background: ios.$ios-primary;
+            color: white;
+            border: none;
+
+            &:hover {
+              background: #0056CC;
+              box-shadow: ios.$ios-shadow-sm;
+            }
+
+            &:disabled {
+              background: ios.$ios-text-tertiary;
+              color: white;
+            }
+          }
+
+          &.mat-stroked-button {
+            border: 1px solid ios.$ios-border;
+            color: ios.$ios-text-secondary;
+
+            &:hover {
+              color: ios.$ios-primary;
+              border-color: ios.$ios-primary;
+              box-shadow: ios.$ios-shadow-sm;
+            }
+          }
+
+          &:active {
+            transform: translateY(1px);
+          }
+        }
+      }
+    }
+
+    .review-records {
+      h4 {
+        margin: 0 0 ios.$ios-spacing-md 0;
+        color: ios.$ios-text-primary;
+        font-size: ios.$ios-font-size-body;
+        font-weight: ios.$ios-font-weight-semibold;
+        font-family: ios.$ios-font-family;
+        display: flex;
+        align-items: center;
+        gap: ios.$ios-spacing-xs;
+
+        &::before {
+          content: '';
+          width: 4px;
+          height: 20px;
+          background: ios.$ios-primary;
+          border-radius: 2px;
+        }
+      }
+
+      .review-record {
+        background: ios.$ios-card-background;
+        border: 1px solid ios.$ios-border;
+        border-radius: ios.$ios-radius-lg;
+        padding: ios.$ios-spacing-lg;
+        margin-bottom: ios.$ios-spacing-md;
+        box-shadow: ios.$ios-shadow-sm;
+        transition: all 0.3s ease;
+
+        &:hover {
+          transform: translateY(-1px);
+          box-shadow: ios.$ios-shadow-md;
+        }
+
+        .review-header {
+          display: flex;
+          align-items: center;
+          gap: ios.$ios-spacing-md;
+          margin-bottom: ios.$ios-spacing-sm;
+
+          .review-type,
+          .review-channel {
+            padding: ios.$ios-spacing-xs ios.$ios-spacing-sm;
+            background-color: rgba(ios.$ios-primary, 0.1);
+            color: ios.$ios-primary;
+            border-radius: ios.$ios-radius-md;
+            font-size: ios.$ios-font-size-caption-1;
+            font-weight: ios.$ios-font-weight-medium;
+            font-family: ios.$ios-font-family;
+          }
+
+          .review-date {
+            color: ios.$ios-text-secondary;
+            font-size: ios.$ios-font-size-subhead;
+            font-family: ios.$ios-font-family;
+          }
+
+          .review-score {
+            display: flex;
+            gap: 2px;
+
+            mat-icon {
+              font-size: 16px;
+              width: 16px;
+              height: 16px;
+              color: ios.$ios-border;
+
+              &.filled {
+                color: ios.$ios-warning;
+              }
+            }
+          }
+        }
+
+        .review-content {
+          background: ios.$ios-background-secondary;
+          border-radius: ios.$ios-radius-md;
+          padding: ios.$ios-spacing-md;
+
+          p {
+            margin: ios.$ios-spacing-xs 0;
+            color: ios.$ios-text-primary;
+            font-size: ios.$ios-font-size-subhead;
+            font-family: ios.$ios-font-family;
+            line-height: 1.5;
+          }
+
+          .review-remark,
+          .review-operator {
+            color: ios.$ios-text-secondary;
+            font-size: ios.$ios-font-size-caption-1;
+            font-family: ios.$ios-font-family;
+          }
+        }
+
+        .follow-up-reminder {
+          display: flex;
+          align-items: center;
+          gap: 8px;
+          margin-top: 12px;
+          padding: 8px;
+          background-color: rgba($warning-color, 0.1);
+          border-radius: $border-radius;
+          color: $warning-color;
+          font-size: 12px;
+
+          mat-icon {
+            font-size: 16px;
+            width: 16px;
+            height: 16px;
+          }
+        }
+      }
+    }
+  }
+
+  // 客户建议收集与处理模块
+  .suggestion-management {
+    h3 {
+      margin: 0 0 ios.$ios-spacing-lg 0;
+      color: ios.$ios-text-primary;
+      font-size: ios.$ios-font-size-title-3;
+      font-weight: ios.$ios-font-weight-semibold;
+      font-family: ios.$ios-font-family;
+      display: flex;
+      align-items: center;
+      gap: ios.$ios-spacing-sm;
+
+      &::before {
+        content: '';
+        width: 4px;
+        height: 24px;
+        background: ios.$ios-primary;
+        border-radius: 2px;
+      }
+    }
+
+    .suggestion-records {
+      .suggestion-record {
+        background: ios.$ios-card-background;
+        border: 1px solid ios.$ios-border;
+        border-radius: ios.$ios-radius-lg;
+        padding: ios.$ios-spacing-lg;
+        margin-bottom: ios.$ios-spacing-md;
+        box-shadow: ios.$ios-shadow-md;
+        transition: all 0.3s ease;
+
+        &:hover {
+          transform: translateY(-2px);
+          box-shadow: ios.$ios-shadow-lg;
+        }
+
+        .suggestion-header {
+          display: flex;
+          align-items: center;
+          gap: ios.$ios-spacing-md;
+          margin-bottom: ios.$ios-spacing-sm;
+
+          .suggestion-category {
+            padding: ios.$ios-spacing-xs ios.$ios-spacing-sm;
+            background-color: rgba(ios.$ios-primary, 0.1);
+            color: ios.$ios-primary;
+            border-radius: ios.$ios-radius-md;
+            font-size: ios.$ios-font-size-caption-1;
+            font-weight: ios.$ios-font-weight-medium;
+            font-family: ios.$ios-font-family;
+          }
+
+          .suggestion-status {
+            padding: ios.$ios-spacing-xs ios.$ios-spacing-sm;
+            border-radius: ios.$ios-radius-md;
+            font-size: ios.$ios-font-size-caption-1;
+            font-weight: ios.$ios-font-weight-medium;
+            font-family: ios.$ios-font-family;
+
+            &.status-pending {
+              background-color: rgba(ios.$ios-warning, 0.1);
+              color: ios.$ios-warning;
+            }
+
+            &.status-processing {
+              background-color: rgba(ios.$ios-primary, 0.1);
+              color: ios.$ios-primary;
+            }
+
+            &.status-completed {
+              background-color: rgba(ios.$ios-success, 0.1);
+              color: ios.$ios-success;
+            }
+
+            &.status-rejected {
+              background-color: rgba(ios.$ios-error, 0.1);
+              color: ios.$ios-error;
+            }
+          }
+
+          .suggestion-priority {
+            padding: ios.$ios-spacing-xs ios.$ios-spacing-sm;
+            border-radius: ios.$ios-radius-md;
+            font-size: ios.$ios-font-size-caption-1;
+            font-weight: ios.$ios-font-weight-medium;
+            font-family: ios.$ios-font-family;
+
+            &.priority-high {
+              background-color: rgba(ios.$ios-error, 0.1);
+              color: ios.$ios-error;
+            }
+
+            &.priority-medium {
+              background-color: rgba(ios.$ios-warning, 0.1);
+              color: ios.$ios-warning;
+            }
+
+            &.priority-low {
+              background-color: rgba(ios.$ios-success, 0.1);
+              color: ios.$ios-success;
+            }
+          }
+
+          .suggestion-date {
+            color: ios.$ios-text-secondary;
+            font-size: ios.$ios-font-size-subhead;
+            font-family: ios.$ios-font-family;
+          }
+        }
+
+        .suggestion-content {
+          background: ios.$ios-background-secondary;
+          border-radius: ios.$ios-radius-md;
+          padding: ios.$ios-spacing-md;
+          margin-bottom: ios.$ios-spacing-sm;
+
+          p {
+            margin: ios.$ios-spacing-xs 0;
+            color: ios.$ios-text-primary;
+            font-size: ios.$ios-font-size-subhead;
+            font-family: ios.$ios-font-family;
+            line-height: 1.5;
+          }
+
+          .suggestion-response {
+            margin-top: ios.$ios-spacing-sm;
+            padding: ios.$ios-spacing-sm;
+            background: ios.$ios-background-tertiary;
+            border-radius: ios.$ios-radius-sm;
+            border-left: 3px solid ios.$ios-primary;
+          }
+        }
+
+        .suggestion-actions {
+          margin-bottom: ios.$ios-spacing-sm;
+
+          .mat-mdc-button {
+            font-family: ios.$ios-font-family;
+            border-radius: ios.$ios-radius-md;
+            padding: ios.$ios-spacing-xs ios.$ios-spacing-md;
+            font-weight: ios.$ios-font-weight-medium;
+            font-size: ios.$ios-font-size-caption-1;
+
+            &.mat-stroked-button {
+              border: 1px solid ios.$ios-border;
+              color: ios.$ios-text-secondary;
+
+              &:hover {
+                color: ios.$ios-primary;
+                border-color: ios.$ios-primary;
+                box-shadow: ios.$ios-shadow-sm;
+              }
+            }
+          }
+        }
+
+        .update-form {
+          background: linear-gradient(135deg, #ffffff 0%, #f8f9fa 100%);
+          border-radius: ios.$ios-radius-lg;
+          margin-top: ios.$ios-spacing-md;
+          margin-bottom: ios.$ios-spacing-sm;
+          border: 1px solid rgba(0, 122, 255, 0.1);
+          box-shadow: 
+            0 8px 32px rgba(0, 0, 0, 0.08),
+            0 2px 8px rgba(0, 0, 0, 0.04);
+          transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
+          position: relative;
+          overflow: hidden;
+
+          &::before {
+            content: '';
+            position: absolute;
+            top: 0;
+            left: 0;
+            right: 0;
+            height: 3px;
+            background: linear-gradient(90deg, #007aff, #5ac8fa);
+            border-radius: ios.$ios-radius-lg ios.$ios-radius-lg 0 0;
+          }
+
+          .update-form-header {
+            display: flex;
+            align-items: center;
+            justify-content: space-between;
+            padding: 16px 20px 12px;
+            border-bottom: 1px solid rgba(0, 122, 255, 0.1);
+            margin-bottom: 16px;
+
+            .header-content {
+              display: flex;
+              align-items: center;
+              gap: 10px;
+
+              .header-icon {
+                color: ios.$ios-primary;
+                font-size: 20px;
+                width: 20px;
+                height: 20px;
+              }
+
+              h5 {
+                margin: 0;
+                color: #1d1d1f;
+                font-size: 16px;
+                font-weight: 600;
+                font-family: ios.$ios-font-family;
+              }
+            }
+
+            .close-button {
+              color: ios.$ios-text-secondary;
+              transition: all 0.2s ease;
+
+              &:hover {
+                color: ios.$ios-primary;
+                background: rgba(0, 122, 255, 0.1);
+              }
+            }
+          }
+
+          .status-update-form {
+            padding: 0 20px 20px;
+
+            .form-grid {
+              display: flex;
+              flex-direction: column;
+              gap: 16px;
+
+              .form-row {
+                display: grid;
+                grid-template-columns: 1fr 1fr;
+                gap: 16px;
+
+                @media (max-width: 768px) {
+                  grid-template-columns: 1fr;
+                }
+              }
+            }
+          }
+
+          // 状态选项样式
+          .status-option {
+            display: flex;
+            align-items: center;
+            gap: 12px;
+
+            .status-icon {
+              font-size: 18px;
+              width: 18px;
+              height: 18px;
+
+              &.pending {
+                color: ios.$ios-warning;
+              }
+
+              &.processing {
+                color: ios.$ios-primary;
+              }
+
+              &.completed {
+                color: ios.$ios-success;
+              }
+
+              &.rejected {
+                color: ios.$ios-error;
+              }
+            }
+
+            .status-info {
+              display: flex;
+              flex-direction: column;
+              gap: 2px;
+
+              .status-name {
+                font-weight: 500;
+                font-size: 14px;
+              }
+
+              .status-desc {
+                font-size: 12px;
+                color: ios.$ios-text-tertiary;
+                opacity: 0.8;
+              }
+            }
+          }
+
+          // 表单字段样式
+          .mat-mdc-form-field {
+            width: 100%;
+            font-family: ios.$ios-font-family;
+            
+            &.status-field {
+              flex: 1;
+            }
+
+            &.assignee-field {
+              flex: 1;
+            }
+
+            &.full-width {
+              grid-column: 1 / -1;
+            }
+
+            // Outline appearance 样式
+            &.mat-mdc-form-field-appearance-outline {
+              .mat-mdc-form-field-outline {
+                border-radius: ios.$ios-radius-md;
+                border-color: ios.$ios-border;
+                transition: all 0.3s ease;
+              }
+
+              &:hover .mat-mdc-form-field-outline {
+                border-color: ios.$ios-primary;
+              }
+
+              &.mat-focused .mat-mdc-form-field-outline {
+                border-color: ios.$ios-primary;
+                border-width: 2px;
+              }
+            }
+
+            .mat-mdc-form-field-infix {
+              color: ios.$ios-text-primary;
+              font-family: ios.$ios-font-family;
+              padding: 12px;
+            }
+
+            // 选择框样式
+            .mat-mdc-select-value {
+              color: ios.$ios-text-primary;
+              font-family: ios.$ios-font-family;
+              font-size: 14px;
+            }
+
+            .mat-mdc-select-arrow {
+              color: ios.$ios-text-secondary;
+              transition: all 0.3s ease;
+            }
+
+            &.mat-focused .mat-mdc-select-arrow {
+              color: ios.$ios-primary;
+              transform: rotate(180deg);
+            }
+
+            // 输入框样式
+            .mat-mdc-input-element {
+              color: ios.$ios-text-primary;
+              font-family: ios.$ios-font-family;
+              font-size: 14px;
+            }
+
+            // 标签样式
+            .mat-mdc-floating-label {
+              color: ios.$ios-text-secondary;
+              font-family: ios.$ios-font-family;
+              font-weight: 500;
+            }
+
+            // 提示文本样式
+            .mat-mdc-form-field-hint {
+              color: ios.$ios-text-tertiary;
+              font-family: ios.$ios-font-family;
+              font-size: 12px;
+            }
+          }
+
+          .form-actions {
+            display: flex;
+            gap: ios.$ios-spacing-sm;
+            justify-content: flex-end;
+            margin-top: ios.$ios-spacing-md;
+            padding-top: ios.$ios-spacing-md;
+            border-top: 1px solid ios.$ios-border;
+
+            .mat-mdc-button {
+              font-family: ios.$ios-font-family;
+              border-radius: ios.$ios-radius-md;
+              padding: ios.$ios-spacing-sm ios.$ios-spacing-lg;
+              font-weight: ios.$ios-font-weight-medium;
+
+              &.mat-primary {
+                background: ios.$ios-primary;
+                color: white;
+                border: none;
+
+                &:hover {
+                  background: #0056CC;
+                  box-shadow: ios.$ios-shadow-sm;
+                }
+              }
+
+              &.mat-stroked-button {
+                border: 1px solid ios.$ios-border;
+                color: ios.$ios-text-secondary;
+
+                &:hover {
+                  color: ios.$ios-primary;
+                  border-color: ios.$ios-primary;
+                  box-shadow: ios.$ios-shadow-sm;
+                }
+              }
+
+              &:active {
+                transform: translateY(1px);
+              }
+            }
+          }
+        }
+
+        .status-history {
+          margin-top: ios.$ios-spacing-md;
+          background: linear-gradient(135deg, #f8f9fa 0%, #ffffff 100%);
+          border-radius: ios.$ios-radius-lg;
+          padding: ios.$ios-spacing-lg;
+          border: 2px solid rgba(0, 122, 255, 0.1);
+          position: relative;
+          overflow: hidden;
+
+          &::before {
+            content: '';
+            position: absolute;
+            top: 0;
+            left: 0;
+            right: 0;
+            height: 4px;
+            background: linear-gradient(90deg, #34c759, #30d158, #32d74b);
+            border-radius: ios.$ios-radius-lg ios.$ios-radius-lg 0 0;
+          }
+
+          h5 {
+            margin: 0 0 ios.$ios-spacing-md 0;
+            color: ios.$ios-text-primary;
+            font-size: ios.$ios-font-size-headline;
+            font-weight: ios.$ios-font-weight-bold;
+            font-family: ios.$ios-font-family;
+            display: flex;
+            align-items: center;
+            gap: ios.$ios-spacing-sm;
+
+            &::before {
+              content: '📋';
+              font-size: 20px;
+            }
+          }
+
+          .history-item {
+            display: grid;
+            grid-template-columns: auto auto auto 1fr;
+            gap: ios.$ios-spacing-md;
+            padding: ios.$ios-spacing-sm 0;
+            font-size: ios.$ios-font-size-caption-1;
+            color: ios.$ios-text-secondary;
+            font-family: ios.$ios-font-family;
+            border-bottom: 1px solid rgba(0, 122, 255, 0.1);
+            transition: all 0.3s ease;
+
+            &:last-child {
+              border-bottom: none;
+            }
+
+            &:hover {
+              background: rgba(0, 122, 255, 0.05);
+              border-radius: ios.$ios-radius-sm;
+              padding-left: ios.$ios-spacing-sm;
+              padding-right: ios.$ios-spacing-sm;
+            }
+
+            .history-date {
+              color: ios.$ios-text-primary;
+              font-weight: ios.$ios-font-weight-semibold;
+              background: rgba(0, 122, 255, 0.1);
+              padding: 4px 8px;
+              border-radius: ios.$ios-radius-sm;
+              font-size: ios.$ios-font-size-caption-2;
+            }
+
+            .history-status {
+              padding: 4px 8px;
+              border-radius: ios.$ios-radius-sm;
+              font-size: ios.$ios-font-size-caption-2;
+              font-weight: ios.$ios-font-weight-medium;
+
+              &.status-pending {
+                background: rgba(255, 149, 0, 0.1);
+                color: #ff9500;
+              }
+
+              &.status-processing {
+                background: rgba(0, 122, 255, 0.1);
+                color: #007aff;
+              }
+
+              &.status-completed {
+                background: rgba(52, 199, 89, 0.1);
+                color: #34c759;
+              }
+
+              &.status-rejected {
+                background: rgba(255, 59, 48, 0.1);
+                color: #ff3b30;
+              }
+            }
+
+            .history-operator {
+              color: ios.$ios-text-primary;
+              font-weight: ios.$ios-font-weight-medium;
+            }
+
+            .history-remark {
+              color: ios.$ios-text-secondary;
+              font-style: italic;
+            }
+          }
+        }
+      }
+    }
+  }
+
+  // 空状态
+  .empty-state {
+    text-align: center;
+    padding: ios.$ios-spacing-xl ios.$ios-spacing-lg;
+    color: ios.$ios-text-secondary;
+    background: ios.$ios-background-secondary;
+    border-radius: ios.$ios-radius-lg;
+    border: 1px solid ios.$ios-border;
+
+    mat-icon {
+      font-size: 48px;
+      width: 48px;
+      height: 48px;
+      margin-bottom: ios.$ios-spacing-md;
+      opacity: 0.5;
+    }
+
+    p {
+      margin: 0;
+      font-size: ios.$ios-font-size-subhead;
+      font-family: ios.$ios-font-family;
+      color: ios.$ios-text-primary;
+    }
+  }
+
+  .detail-content {
+    background: ios.$ios-card-background;
+    border-radius: ios.$ios-radius-lg;
+    padding: ios.$ios-spacing-lg;
+    box-shadow: ios.$ios-shadow-sm;
+    border: 1px solid ios.$ios-border;
+  }
+}
+
+// 人事板块下拉列表样式
+.ios-select {
+  .mat-mdc-form-field-outline {
+    border-radius: 12px !important;
+    border: 1px solid #e0e0e0 !important;
+    background-color: #f8f9fa !important;
+    
+    &:hover {
+      border-color: #007aff !important;
+      background-color: #ffffff !important;
+    }
+  }
+  
+  .mat-mdc-select-trigger {
+    border-radius: 12px;
+    padding: 12px 16px;
+    min-height: 48px;
+    display: flex;
+    align-items: center;
+    
+    &:hover {
+      background-color: rgba(0, 122, 255, 0.05);
+    }
+  }
+  
+  .mat-mdc-select-arrow {
+    color: #007aff;
+    font-size: 20px;
+  }
+  
+  .mat-mdc-select-value {
+    color: #333;
+    font-size: 16px;
+    font-weight: 500;
+  }
+}
+
+.option-content {
+  display: flex;
+  align-items: center;
+  gap: 12px;
+  
+  mat-icon {
+    color: #007aff;
+    font-size: 18px;
+    width: 18px;
+    height: 18px;
+  }
+  
+  span {
+    font-size: 14px;
+    color: #333;
+  }
+}
+
+.filter-actions-group {
+  display: flex;
+  gap: 12px;
+  margin-bottom: 20px;
+  
+  .ios-select {
+    flex: 1;
+    min-width: 200px;
+  }
+}
+
+// 全局下拉面板样式 - 采用人事板块样式,增加透明度
+:host ::ng-deep .mat-mdc-select-panel {
+  background: rgba(255, 255, 255, 0.85) !important;
+  backdrop-filter: blur(20px) !important;
+  border-radius: 12px !important;
+  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.15) !important;
+  border: 1px solid rgba(0, 122, 255, 0.2) !important;
+  max-height: 300px !important;
+  z-index: 9999 !important;
+  overflow: hidden !important;
+  
+  .mat-mdc-option {
+    background: rgba(255, 255, 255, 0.3) !important;
+    color: #333 !important;
+    font-size: 16px !important;
+    font-family: ios.$ios-font-family !important;
+    padding: 14px 16px !important;
+    border-bottom: 1px solid rgba(0, 122, 255, 0.1) !important;
+    transition: all 0.3s ease !important;
+    min-height: 48px !important;
+    display: flex !important;
+    align-items: center !important;
+    
+    &:hover {
+      background: rgba(0, 122, 255, 0.15) !important;
+      color: #007AFF !important;
+      transform: translateX(2px) !important;
+    }
+    
+    &.mdc-list-item--selected,
+    &.mat-selected {
+      background: rgba(0, 122, 255, 0.2) !important;
+      color: #007AFF !important;
+      font-weight: 500 !important;
+    }
+    
+    &:last-child {
+      border-bottom: none !important;
+    }
+
+    // 选项内容样式
+    .option-content {
+      display: flex;
+      align-items: center;
+      gap: 10px;
+      width: 100%;
+
+      mat-icon {
+        font-size: 18px;
+        width: 18px;
+        height: 18px;
+        color: inherit;
+        opacity: 0.8;
+      }
+
+      span {
+        flex: 1;
+        color: inherit;
+        font-size: 16px;
+        line-height: 1.4;
+      }
+    }
+  }
+}
+
+// 响应式设计
+@media (max-width: 768px) {
+  .after-sales-container {
+    padding: ios.$ios-spacing-md;
+  }
+
+  .stats-dashboard {
+    grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
+    gap: ios.$ios-spacing-sm;
+
+    .stat-card {
+      padding: ios.$ios-spacing-md;
+      gap: ios.$ios-spacing-sm;
+
+      .stat-icon {
+        width: 40px;
+        height: 40px;
+
+        mat-icon {
+          font-size: 20px;
+          width: 20px;
+          height: 20px;
+        }
+      }
+
+      .stat-content .stat-value {
+        font-size: 20px;
+      }
+    }
+  }
+
+  .filter-bar {
+    padding: ios.$ios-spacing-md;
+
+    .filter-section {
+      flex-direction: column;
+      gap: ios.$ios-spacing-sm;
+
+      .filter-field {
+        width: 100%;
+      }
+    }
+  }
+
+  .project-card {
+    padding: ios.$ios-spacing-md;
+
+    .project-content {
+      grid-template-columns: 1fr;
+      gap: ios.$ios-spacing-md;
+    }
+
+    .project-footer {
+      flex-direction: column;
+      align-items: flex-start;
+      gap: ios.$ios-spacing-xs;
+    }
+  }
+
+  .detail-view {
+    .suggestion-management {
+      .suggestion-record {
+        padding: ios.$ios-spacing-md;
+
+        .suggestion-header {
+          flex-direction: column;
+          align-items: flex-start;
+          gap: ios.$ios-spacing-xs;
+        }
+
+        .update-form {
+          padding: ios.$ios-spacing-md;
+
+          .form-actions {
+            flex-direction: column;
+            gap: ios.$ios-spacing-xs;
+
+            .mat-mdc-button {
+              width: 100%;
+            }
+          }
+        }
+
+        .status-history {
+          padding: ios.$ios-spacing-sm;
+
+          .history-item {
+            grid-template-columns: 1fr;
+            gap: ios.$ios-spacing-xs;
+            text-align: left;
+          }
+        }
+      }
+    }
+
+    .review-management {
+      .review-record {
+        padding: ios.$ios-spacing-md;
+
+        .review-header {
+          flex-direction: column;
+          align-items: flex-start;
+          gap: ios.$ios-spacing-xs;
+        }
+      }
+
+      .add-review-form {
+        padding: ios.$ios-spacing-md;
+
+        .form-actions {
+          flex-direction: column;
+          gap: ios.$ios-spacing-xs;
+
+          .mat-mdc-button {
+            width: 100%;
+          }
+        }
+      }
+    }
+
+    .payment-management {
+      .payment-summary {
+        grid-template-columns: 1fr;
+        gap: ios.$ios-spacing-sm;
+      }
+
+      .payment-record {
+        padding: ios.$ios-spacing-md;
+
+        .record-header {
+          flex-direction: column;
+          align-items: flex-start;
+          gap: ios.$ios-spacing-xs;
+        }
+      }
+    }
+  }
+}

+ 621 - 0
src/app/pages/customer-service/dashboard/pages/after-sales/after-sales.component.ts

@@ -0,0 +1,621 @@
+import { Component, OnInit, signal, computed } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FormsModule } from '@angular/forms';
+import { RouterModule, Router } from '@angular/router';
+import { MatIconModule } from '@angular/material/icon';
+import { MatButtonModule } from '@angular/material/button';
+import { MatFormFieldModule } from '@angular/material/form-field';
+import { MatInputModule } from '@angular/material/input';
+import { MatSelectModule } from '@angular/material/select';
+import { MatDatepickerModule } from '@angular/material/datepicker';
+import { MatNativeDateModule } from '@angular/material/core';
+
+export interface AfterSalesProject {
+  id: string;
+  projectCode: string;
+  projectName: string;
+  customerName: string;
+  completedDate: Date;
+  finalPaymentStatus: 'pending' | 'partial' | 'completed' | 'overdue';
+  finalPaymentAmount: number;
+  paidAmount: number;
+  reviewStatus: 'pending' | 'positive' | 'negative' | 'no-review';
+  reviewScore?: number;
+  reviewContent?: string;
+  suggestionCount: number;
+  lastContactDate: Date;
+  priority: 'high' | 'medium' | 'low';
+  // 详情页面相关数据
+  paymentRecords?: PaymentRecord[];
+  reviewRecords?: ReviewRecord[];
+  suggestions?: CustomerSuggestion[];
+}
+
+export interface PaymentRecord {
+  id: string;
+  amount: number;
+  paymentDate: Date;
+  paymentMethod: string;
+  voucher?: FileUpload;
+  status: 'pending' | 'verified' | 'rejected';
+  operator: string;
+  remark?: string;
+  statusHistory: PaymentStatusHistory[];
+}
+
+export interface PaymentStatusHistory {
+  id: string;
+  status: string;
+  operator: string;
+  changeDate: Date;
+  reason: string;
+}
+
+export interface FileUpload {
+  id: string;
+  fileName: string;
+  fileType: string;
+  fileSize: number;
+  uploadDate: Date;
+  fileUrl: string;
+  previewUrl?: string;
+}
+
+export interface ReviewRecord {
+  id: string;
+  reviewType: 'system' | 'manual';
+  reviewChannel: string;
+  reviewDate: Date;
+  score?: number;
+  content: string;
+  status: 'pending' | 'processed';
+  followUpReminder?: Date;
+  operator?: string;
+  remark?: string;
+}
+
+export interface CustomerSuggestion {
+  id: string;
+  category: string;
+  content: string;
+  submitDate: Date;
+  status: 'pending' | 'processing' | 'completed' | 'rejected';
+  priority: 'high' | 'medium' | 'low';
+  assignee?: string;
+  response?: string;
+  responseDate?: Date;
+  statusHistory: SuggestionStatusHistory[];
+}
+
+export interface SuggestionStatusHistory {
+  id: string;
+  status: string;
+  operator: string;
+  changeDate: Date;
+  remark: string;
+}
+
+@Component({
+  selector: 'app-after-sales',
+  standalone: true,
+  imports: [
+    CommonModule, 
+    FormsModule, 
+    RouterModule,
+    MatIconModule,
+    MatButtonModule,
+    MatFormFieldModule,
+    MatInputModule,
+    MatSelectModule,
+    MatDatepickerModule,
+    MatNativeDateModule
+  ],
+  templateUrl: './after-sales.component.html',
+  styleUrls: ['./after-sales.component.scss']
+})
+export class AfterSalesComponent implements OnInit {
+  // 搜索和筛选条件
+  searchTerm = signal('');
+  selectedPaymentStatus = signal('all');
+  selectedReviewStatus = signal('all');
+  selectedPriority = signal('all');
+  dateRange = signal<{ start: Date | null; end: Date | null }>({ start: null, end: null });
+
+  // 售后项目数据
+  afterSalesProjects = signal<AfterSalesProject[]>([]);
+  
+  // 当前选中的项目
+  selectedProject = signal<AfterSalesProject | null>(null);
+  
+  // 视图状态
+  currentView = signal<'overview' | 'detail'>('overview');
+  
+  // 详情页面状态
+  activeDetailTab = signal<'payment' | 'review' | 'suggestion'>('payment');
+  
+  // 表单状态
+  isUploadingFile = signal(false);
+  isSubmittingForm = signal(false);
+  showAddReviewForm = false;
+  showUpdateForm: { [key: string]: boolean } = {};
+  
+  // 建议类别选项
+  suggestionCategories = signal([
+    { value: 'service-process', label: '服务流程' },
+    { value: 'product-function', label: '产品功能' },
+    { value: 'delivery-time', label: '交付周期' },
+    { value: 'communication', label: '沟通协调' },
+    { value: 'quality', label: '质量问题' },
+    { value: 'other', label: '其他建议' }
+  ]);
+  
+  // 筛选后的项目列表
+  filteredProjects = computed(() => {
+    let projects = this.afterSalesProjects();
+    
+    // 搜索筛选
+    if (this.searchTerm()) {
+      const term = this.searchTerm().toLowerCase();
+      projects = projects.filter(p => 
+        p.projectCode.toLowerCase().includes(term) ||
+        p.projectName.toLowerCase().includes(term) ||
+        p.customerName.toLowerCase().includes(term)
+      );
+    }
+    
+    // 尾款状态筛选
+    if (this.selectedPaymentStatus() !== 'all') {
+      projects = projects.filter(p => p.finalPaymentStatus === this.selectedPaymentStatus());
+    }
+    
+    // 评价状态筛选
+    if (this.selectedReviewStatus() !== 'all') {
+      projects = projects.filter(p => p.reviewStatus === this.selectedReviewStatus());
+    }
+    
+    // 优先级筛选
+    if (this.selectedPriority() !== 'all') {
+      projects = projects.filter(p => p.priority === this.selectedPriority());
+    }
+    
+    return projects;
+  });
+
+  // 统计数据
+  stats = computed(() => {
+    const projects = this.afterSalesProjects();
+    return {
+      total: projects.length,
+      pendingPayment: projects.filter(p => p.finalPaymentStatus === 'pending').length,
+      overduePayment: projects.filter(p => p.finalPaymentStatus === 'overdue').length,
+      pendingReview: projects.filter(p => p.reviewStatus === 'pending').length,
+      highPriority: projects.filter(p => p.priority === 'high').length
+    };
+  });
+
+  constructor(private router: Router) {}
+
+  ngOnInit(): void {
+    this.loadAfterSalesData();
+  }
+
+  private loadAfterSalesData(): void {
+    // 模拟数据
+    const mockData: AfterSalesProject[] = [
+      {
+        id: '1',
+        projectCode: 'PRJ-2024-001',
+        projectName: '企业官网设计开发',
+        customerName: '张三科技有限公司',
+        completedDate: new Date('2024-01-15'),
+        finalPaymentStatus: 'pending',
+        finalPaymentAmount: 50000,
+        paidAmount: 40000,
+        reviewStatus: 'positive',
+        reviewScore: 5,
+        reviewContent: '非常满意,设计很棒!',
+        suggestionCount: 2,
+        lastContactDate: new Date('2024-01-20'),
+        priority: 'high'
+      },
+      {
+        id: '2',
+        projectCode: 'PRJ-2024-002',
+        projectName: '移动端APP开发',
+        customerName: '李四网络科技',
+        completedDate: new Date('2024-01-10'),
+        finalPaymentStatus: 'overdue',
+        finalPaymentAmount: 80000,
+        paidAmount: 60000,
+        reviewStatus: 'pending',
+        suggestionCount: 1,
+        lastContactDate: new Date('2024-01-18'),
+        priority: 'high'
+      },
+      {
+        id: '3',
+        projectCode: 'PRJ-2024-003',
+        projectName: '电商平台升级',
+        customerName: '王五电商',
+        completedDate: new Date('2024-01-08'),
+        finalPaymentStatus: 'completed',
+        finalPaymentAmount: 120000,
+        paidAmount: 120000,
+        reviewStatus: 'positive',
+        reviewScore: 4,
+        reviewContent: '整体不错,有些细节需要优化',
+        suggestionCount: 3,
+        lastContactDate: new Date('2024-01-22'),
+        priority: 'medium'
+      }
+    ];
+    
+    this.afterSalesProjects.set(mockData);
+  }
+
+  // 选择项目进入详情页
+  selectProject(project: AfterSalesProject): void {
+    this.selectedProject.set(project);
+    this.currentView.set('detail');
+    this.loadProjectDetails(project.id);
+  }
+
+  // 返回概览页
+  backToOverview(): void {
+    this.currentView.set('overview');
+    this.selectedProject.set(null);
+    this.activeDetailTab.set('payment');
+  }
+
+  // 切换详情页面标签
+  switchDetailTab(tab: 'payment' | 'review' | 'suggestion'): void {
+    this.activeDetailTab.set(tab);
+  }
+
+  // 加载项目详情数据
+  async loadProjectDetails(projectId: string): Promise<void> {
+    try {
+      // 模拟API调用
+      const projectDetails = await this.mockLoadProjectDetails(projectId);
+      const currentProject = this.selectedProject();
+      if (currentProject) {
+        this.selectedProject.set({
+          ...currentProject,
+          ...projectDetails
+        });
+      }
+    } catch (error) {
+      console.error('加载项目详情失败:', error);
+    }
+  }
+
+  // 模拟加载项目详情数据
+  private async mockLoadProjectDetails(projectId: string) {
+    // 模拟API延迟
+    await new Promise(resolve => setTimeout(resolve, 500));
+    
+    return {
+      paymentRecords: [
+        {
+          id: '1',
+          amount: 5000,
+          paymentDate: new Date('2024-01-15'),
+          paymentMethod: '银行转账',
+          status: 'verified' as const,
+          operator: '张三',
+          remark: '首期尾款',
+          statusHistory: [
+            {
+              id: '1',
+              status: '待验证',
+              operator: '系统',
+              changeDate: new Date('2024-01-15'),
+              reason: '客户上传支付凭证'
+            },
+            {
+              id: '2',
+              status: '已验证',
+              operator: '张三',
+              changeDate: new Date('2024-01-16'),
+              reason: '核实支付信息无误'
+            }
+          ]
+        }
+      ],
+      reviewRecords: [
+        {
+          id: '1',
+          reviewType: 'manual' as const,
+          reviewChannel: '电话回访',
+          reviewDate: new Date('2024-01-20'),
+          score: 5,
+          content: '服务很满意,交付及时',
+          status: 'processed' as const,
+          operator: '李四',
+          remark: '客户反馈良好'
+        }
+      ],
+      suggestions: [
+        {
+          id: '1',
+          category: 'service-process',
+          content: '希望能提供更详细的项目进度报告',
+          submitDate: new Date('2024-01-18'),
+          status: 'processing' as const,
+          priority: 'medium' as const,
+          assignee: '王五',
+          statusHistory: [
+            {
+              id: '1',
+              status: '待处理',
+              operator: '系统',
+              changeDate: new Date('2024-01-18'),
+              remark: '客户提交建议'
+            },
+            {
+              id: '2',
+              status: '处理中',
+              operator: '王五',
+              changeDate: new Date('2024-01-19'),
+              remark: '已分配处理人员'
+            }
+          ]
+        }
+      ]
+    };
+  }
+
+  // 文件上传处理
+  async handleFileUpload(event: Event, recordId?: string): Promise<void> {
+    const input = event.target as HTMLInputElement;
+    const files = input.files;
+    
+    if (!files || files.length === 0) return;
+    
+    this.isUploadingFile.set(true);
+    
+    try {
+      for (const file of Array.from(files)) {
+        await this.uploadFile(file, recordId);
+      }
+      // 显示成功提示
+      this.showSuccessMessage('文件上传成功');
+    } catch (error) {
+      console.error('文件上传失败:', error);
+      this.showErrorMessage('文件上传失败,请检查文件格式和大小');
+    } finally {
+      this.isUploadingFile.set(false);
+    }
+  }
+
+  // 上传文件
+  private async uploadFile(file: File, recordId?: string): Promise<FileUpload> {
+    // 验证文件类型和大小
+    const allowedTypes = ['image/jpeg', 'image/png', 'image/gif', 'application/pdf'];
+    const maxSize = 10 * 1024 * 1024; // 10MB
+    
+    if (!allowedTypes.includes(file.type)) {
+      throw new Error('文件格式不支持,请上传 JPG/PNG/PDF 格式');
+    }
+    
+    if (file.size > maxSize) {
+      throw new Error('文件大小超过限制,请上传小于10MB的文件');
+    }
+    
+    // 模拟上传过程
+    await new Promise(resolve => setTimeout(resolve, 1000));
+    
+    return {
+      id: Date.now().toString(),
+      fileName: file.name,
+      fileType: file.type,
+      fileSize: file.size,
+      uploadDate: new Date(),
+      fileUrl: URL.createObjectURL(file),
+      previewUrl: file.type.startsWith('image/') ? URL.createObjectURL(file) : undefined
+    };
+  }
+
+  // 添加评价记录
+  async addReviewRecord(reviewData: Partial<ReviewRecord>): Promise<void> {
+    this.isSubmittingForm.set(true);
+    
+    try {
+      // 模拟API调用
+      await new Promise(resolve => setTimeout(resolve, 500));
+      
+      const newRecord: ReviewRecord = {
+        id: Date.now().toString(),
+        reviewType: 'manual',
+        reviewChannel: reviewData.reviewChannel || '',
+        reviewDate: new Date(),
+        score: reviewData.score,
+        content: reviewData.content || '',
+        status: 'processed',
+        operator: '当前用户',
+        remark: reviewData.remark
+      };
+      
+      const currentProject = this.selectedProject();
+      if (currentProject && currentProject.reviewRecords) {
+        currentProject.reviewRecords.push(newRecord);
+        this.selectedProject.set({ ...currentProject });
+      }
+      
+      this.showSuccessMessage('评价记录添加成功');
+    } catch (error) {
+      console.error('添加评价记录失败:', error);
+      this.showErrorMessage('添加评价记录失败,请重试');
+    } finally {
+      this.isSubmittingForm.set(false);
+    }
+  }
+
+  // 更新建议状态
+  async updateSuggestionStatus(suggestionId: string, status: string, response?: string): Promise<void> {
+    this.isSubmittingForm.set(true);
+    
+    try {
+      // 模拟API调用
+      await new Promise(resolve => setTimeout(resolve, 500));
+      
+      const currentProject = this.selectedProject();
+      if (currentProject && currentProject.suggestions) {
+        const suggestion = currentProject.suggestions.find(s => s.id === suggestionId);
+        if (suggestion) {
+          suggestion.status = status as any;
+          suggestion.response = response;
+          suggestion.responseDate = new Date();
+          
+          // 添加状态历史
+          suggestion.statusHistory.push({
+            id: Date.now().toString(),
+            status,
+            operator: '当前用户',
+            changeDate: new Date(),
+            remark: response || '状态更新'
+          });
+          
+          this.selectedProject.set({ ...currentProject });
+        }
+      }
+      
+      this.showSuccessMessage('建议状态更新成功');
+    } catch (error) {
+      console.error('更新建议状态失败:', error);
+      this.showErrorMessage('更新建议状态失败,请重试');
+    } finally {
+      this.isSubmittingForm.set(false);
+    }
+  }
+
+  // 显示成功消息
+  private showSuccessMessage(message: string): void {
+    // 这里可以集成实际的消息提示组件
+    alert(message);
+  }
+
+  // 显示错误消息
+  private showErrorMessage(message: string): void {
+    // 这里可以集成实际的消息提示组件
+    alert(message);
+  }
+  
+  // 获取支付记录状态文本
+  getPaymentRecordStatusText(status: string): string {
+    const statusMap: { [key: string]: string } = {
+      'pending': '待验证',
+      'verified': '已验证',
+      'rejected': '已拒绝'
+    };
+    return statusMap[status] || status;
+  }
+  
+  // 获取建议状态文本
+  getSuggestionStatusText(status: string): string {
+    const statusMap: { [key: string]: string } = {
+      'pending': '待处理',
+      'processing': '处理中',
+      'completed': '已完成',
+      'rejected': '已拒绝'
+    };
+    return statusMap[status] || status;
+  }
+  
+  // 获取建议类别标签
+  getSuggestionCategoryLabel(category: string): string {
+    const categoryMap: { [key: string]: string } = {
+      'service-process': '服务流程',
+      'product-function': '产品功能',
+      'delivery-time': '交付周期',
+      'communication': '沟通协调',
+      'quality': '质量问题',
+      'other': '其他建议'
+    };
+    return categoryMap[category] || category;
+  }
+
+  // 获取尾款状态显示文本
+  getPaymentStatusText(status: string): string {
+    const statusMap: Record<string, string> = {
+      'pending': '待收款',
+      'partial': '部分收款',
+      'completed': '已完成',
+      'overdue': '逾期'
+    };
+    return statusMap[status] || status;
+  }
+
+  // 获取尾款状态样式类
+  getPaymentStatusClass(status: string): string {
+    const classMap: Record<string, string> = {
+      'pending': 'status-pending',
+      'partial': 'status-partial',
+      'completed': 'status-completed',
+      'overdue': 'status-overdue'
+    };
+    return classMap[status] || '';
+  }
+
+  // 获取评价状态显示文本
+  getReviewStatusText(status: string): string {
+    const statusMap: Record<string, string> = {
+      'pending': '待评价',
+      'positive': '好评',
+      'negative': '差评',
+      'no-review': '无评价'
+    };
+    return statusMap[status] || status;
+  }
+
+  // 获取评价状态样式类
+  getReviewStatusClass(status: string): string {
+    const classMap: Record<string, string> = {
+      'pending': 'review-pending',
+      'positive': 'review-positive',
+      'negative': 'review-negative',
+      'no-review': 'review-none'
+    };
+    return classMap[status] || '';
+  }
+
+  // 获取优先级显示文本
+  getPriorityText(priority: string): string {
+    const priorityMap: Record<string, string> = {
+      'high': '高',
+      'medium': '中',
+      'low': '低'
+    };
+    return priorityMap[priority] || priority;
+  }
+
+  // 获取优先级样式类
+  getPriorityClass(priority: string): string {
+    const classMap: Record<string, string> = {
+      'high': 'priority-high',
+      'medium': 'priority-medium',
+      'low': 'priority-low'
+    };
+    return classMap[priority] || '';
+  }
+
+  // 格式化金额
+  formatAmount(amount: number): string {
+    return `¥${amount.toLocaleString()}`;
+  }
+
+  // 格式化货币
+  formatCurrency(amount: number): string {
+    return `¥${amount.toLocaleString()}`;
+  }
+
+  // 格式化日期
+  formatDate(date: Date): string {
+    return date.toLocaleDateString('zh-CN');
+  }
+
+  // 计算尾款进度百分比
+  getPaymentProgress(project: AfterSalesProject): number {
+    return Math.round((project.paidAmount / project.finalPaymentAmount) * 100);
+  }
+}

+ 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 {

+ 80 - 63
src/app/pages/customer-service/dashboard/pages/consultation-list/consultation-list.component.html

@@ -1,82 +1,99 @@
-@if (consultations && consultations.length > 0) {
-  <div class="ios-container">
-    <header class="ios-header">
-      <button class="ios-back-btn" (click)="goBack()">
-        <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor">
-          <path d="M19 12H5M12 19l-7-7 7-7"/>
-        </svg>
-      </button>
-      <h1>客户咨询记录</h1>
-    </header>
+<div class="ios-container">
+  <!-- iOS风格头部导航 -->
+  <div class="ios-header">
+    <button class="ios-back-btn" (click)="goBack()">
+      <svg viewBox="0 0 24 24" fill="currentColor">
+        <path d="M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z"/>
+      </svg>
+    </button>
+    <h1>客户咨询</h1>
+  </div>
 
-    <div class="ios-content">
-      <div class="search-bar">
-        <input type="text" placeholder="搜索咨询记录..." [(ngModel)]="searchKeyword" (input)="filterConsultations()">
-        <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor">
-          <circle cx="11" cy="11" r="8"/>
-          <line x1="21" y1="21" x2="16.65" y2="16.65"/>
-        </svg>
-      </div>
+  <!-- iOS风格内容区域 -->
+  <div class="ios-content">
+    <!-- iOS风格搜索栏 -->
+    <div class="search-bar">
+      <svg viewBox="0 0 24 24" fill="currentColor">
+        <path d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/>
+      </svg>
+      <input 
+        type="text" 
+        placeholder="搜索客户姓名或咨询内容..."
+        [(ngModel)]="searchTerm"
+        (input)="onSearch()"
+      />
+    </div>
 
+    <!-- 咨询列表 -->
+    @if (filteredConsultations.length > 0) {
       <div class="consultation-list">
-        @for (item of filteredConsultations; track item.id) {
-          <div class="consultation-card" [class.urgent]="item.priority === 'high'">
+        @for (consultation of filteredConsultations; track consultation.id) {
+          <div 
+            class="consultation-card"
+            [class.urgent]="consultation.priority === 'urgent'"
+            (click)="viewConsultation(consultation)"
+          >
+            <!-- 卡片头部 -->
             <div class="card-header">
               <div class="customer-info">
-                <div class="avatar" [style.background-color]="getAvatarColor(item.customer)">
-                  {{item.customer.charAt(0)}}
+                <div class="avatar">
+                  {{ consultation.customerName.charAt(0) }}
                 </div>
                 <div>
-                  <h3>{{item.customer}}</h3>
-                  <p class="time">{{item.time}}</p>
+                  <h3>{{ consultation.customerName }}</h3>
+                  <p class="time">{{ consultation.createdAt | date:'MM-dd HH:mm' }}</p>
                 </div>
               </div>
-              <span class="status-badge" [class.urgent]="item.priority === 'high'">
-                {{item.priority === 'high' ? '紧急' : '普通'}}
-              </span>
+              <div 
+                class="status-badge"
+                [class.urgent]="consultation.priority === 'urgent'"
+              >
+                @if (consultation.priority === 'urgent') {
+                  紧急
+                } @else {
+                  普通
+                }
+              </div>
             </div>
-            <p class="content">{{item.content}}</p>
+
+            <!-- 咨询内容 -->
+            <p class="content">{{ consultation.content }}</p>
+
+            <!-- 卡片底部操作按钮 -->
             <div class="card-footer">
-              <button class="ios-btn primary" (click)="viewDetails(item)">查看详情</button>
-              <button class="ios-btn secondary" (click)="assignHandler(item)">分配处理</button>
+              <button 
+                class="ios-btn primary"
+                (click)="handleConsultation(consultation, $event)"
+              >
+                处理咨询
+              </button>
+              <button 
+                class="ios-btn secondary"
+                (click)="viewDetails(consultation, $event)"
+              >
+                查看详情
+              </button>
             </div>
           </div>
         }
       </div>
-
-      @if (filteredConsultations.length === 0 && searchKeyword) {
-        <div class="empty-state">
-          <svg width="64" height="64" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1">
-            <circle cx="11" cy="11" r="8"></circle>
-            <path d="m21 21-4.35-4.35"></path>
+    } @else {
+      <!-- 空状态 -->
+      <div class="empty-state">
+        @if (searchTerm) {
+          <svg width="64" height="64" viewBox="0 0 24 24" fill="currentColor">
+            <path d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/>
           </svg>
           <h3>未找到匹配记录</h3>
-          <p>尝试其他关键词搜索</p>
-        </div>
-      }
-    </div>
-  </div>
-}
-@else {
-  <div class="ios-container">
-    <header class="ios-header">
-      <button class="ios-back-btn" (click)="goBack()">
-        <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor">
-          <path d="M19 12H5M12 19l-7-7 7-7"/>
-        </svg>
-      </button>
-      <h1>客户咨询记录</h1>
-    </header>
-
-    <div class="ios-content">
-      <div class="empty-state">
-        <svg width="80" height="80" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1">
-          <circle cx="11" cy="11" r="8"></circle>
-          <path d="m21 21-4.35-4.35"></path>
-        </svg>
-        <h3>暂无咨询记录</h3>
-        <p>还没有客户咨询记录</p>
+          <p>请尝试其他搜索关键词</p>
+        } @else {
+          <svg width="64" height="64" viewBox="0 0 24 24" fill="currentColor">
+            <path d="M20 6h-2.18c.11-.31.18-.65.18-1a2.996 2.996 0 0 0-5.5-1.65l-.5.67-.5-.68C10.96 2.54 10.05 2 9 2 7.34 2 6 3.34 6 5c0 .35.07.69.18 1H4c-1.11 0-1.99.89-1.99 2L2 19c0 1.11.89 2 2 2h16c1.11 0 2-.89 2-2V8c0-1.11-.89-2-2-2zm-5-2c.55 0 1 .45 1 1s-.45 1-1 1-1-.45-1-1 .45-1 1-1zM9 4c.55 0 1 .45 1 1s-.45 1-1 1-1-.45-1-1 .45-1 1-1z"/>
+          </svg>
+          <h3>暂无咨询记录</h3>
+          <p>当前没有客户咨询记录</p>
+        }
       </div>
-    </div>
+    }
   </div>
-}
+</div>

+ 207 - 687
src/app/pages/customer-service/dashboard/pages/consultation-list/consultation-list.component.scss

@@ -1,13 +1,6 @@
 @import "../../../customer-service-styles.scss";
 
-// 全部客户咨询页面 - iOS风格
-.consultation-container {
-  min-height: 100vh;
-  background: linear-gradient(135deg, #f2f2f7 0%, #e5e5ea 100%);
-  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
-}
-
-// iOS容器样式
+// iOS风格咨询列表页面
 .ios-container {
   min-height: 100vh;
   background: linear-gradient(135deg, #f2f2f7 0%, #e5e5ea 100%);
@@ -16,162 +9,61 @@
 
 // iOS头部导航
 .ios-header {
-  background: linear-gradient(135deg, #ffffff 0%, #f8f9fa 100%);
+  background: rgba(255, 255, 255, 0.95);
+  backdrop-filter: blur(20px);
+  -webkit-backdrop-filter: blur(20px);
   border-bottom: 1px solid rgba(0, 0, 0, 0.05);
   padding: 16px 20px;
   display: flex;
   align-items: center;
-  gap: 12px;
+  gap: 16px;
   position: sticky;
   top: 0;
   z-index: 100;
-  box-shadow: 0 2px 20px rgba(0, 0, 0, 0.08);
-  backdrop-filter: blur(20px);
-  -webkit-backdrop-filter: blur(20px);
+  box-shadow: 0 1px 20px rgba(0, 0, 0, 0.08);
 
   .ios-back-btn {
+    background: rgba(120, 120, 128, 0.12);
+    border: none;
+    border-radius: 10px;
+    padding: 8px;
+    color: #007AFF;
+    cursor: pointer;
+    transition: all 0.2s ease;
     display: 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);
-
+    
     &: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: rgba(120, 120, 128, 0.16);
+      transform: scale(1.05);
     }
-
+    
     &: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: scale(0.95);
+      background: rgba(120, 120, 128, 0.2);
+    }
+
+    svg {
+      width: 20px;
+      height: 20px;
     }
   }
 
   h1 {
-    font-size: 24px;
-    font-weight: 700;
+    font-size: 22px;
+    font-weight: 600;
     color: #1d1d1f;
     margin: 0;
-    line-height: 1.2;
     letter-spacing: -0.3px;
   }
 }
 
-// 页面头部 - iOS风格
-.page-header {
-  background: linear-gradient(135deg, #ffffff 0%, #f8f9fa 100%);
-  border-bottom: 1px solid rgba(0, 0, 0, 0.05);
-  padding: 20px 24px;
-  display: flex;
-  align-items: center;
-  justify-content: space-between;
-  position: sticky;
-  top: 0;
-  z-index: 100;
-  box-shadow: 0 2px 20px rgba(0, 0, 0, 0.08);
-  backdrop-filter: blur(20px);
-  -webkit-backdrop-filter: blur(20px);
-
-  .header-left {
-    display: flex;
-    align-items: center;
-    gap: 16px;
-  }
-
-  .back-btn {
-    display: 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);
-
-    &: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);
-    }
-
-    &: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);
-    }
-  }
-
-  .header-title {
-    h1 {
-      font-size: 28px;
-      font-weight: 700;
-      color: #1d1d1f;
-      margin: 0;
-      line-height: 1.2;
-      letter-spacing: -0.5px;
-    }
-
-    .subtitle {
-      font-size: 15px;
-      color: #8e8e93;
-      margin: 6px 0 0;
-      line-height: 1.4;
-      font-weight: 500;
-    }
-  }
-
-  .header-actions {
-    .refresh-btn {
-      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: 12px;
-      color: #007AFF;
-      font-size: 14px;
-      font-weight: 600;
-      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);
-
-      &:hover:not(:disabled) {
-        background: linear-gradient(135deg, #007AFF 0%, #0062CC 100%);
-        border-color: rgba(0, 0, 0, 0.1);
-        color: #ffffff;
-        transform: translateY(-1px);
-        box-shadow: 0 4px 16px rgba(0, 122, 255, 0.3), inset 0 1px 2px rgba(255, 255, 255, 0.2);
-      }
-
-      &:disabled {
-        opacity: 0.6;
-        cursor: not-allowed;
-      }
-
-      svg.spinning {
-        animation: spin 1s linear infinite;
-      }
-    }
-  }
-}
-
 // iOS内容区域
 .ios-content {
-  padding: 20px;
+  padding: 16px 20px 32px;
   max-width: 800px;
   margin: 0 auto;
 }
@@ -179,672 +71,300 @@
 // iOS搜索栏
 .search-bar {
   position: relative;
-  margin-bottom: 24px;
+  margin-bottom: 20px;
 
   input {
     width: 100%;
-    padding: 16px 48px 16px 16px;
-    border: 1px solid rgba(0, 0, 0, 0.1);
-    border-radius: 16px;
+    padding: 12px 16px 12px 44px;
+    border: none;
+    border-radius: 12px;
     font-size: 16px;
-    background: linear-gradient(135deg, #ffffff 0%, #f8f9fa 100%);
-    transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
+    background: rgba(120, 120, 128, 0.12);
+    transition: all 0.3s ease;
     outline: none;
-    box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.05), 0 1px 2px rgba(255, 255, 255, 0.8);
+    color: #1d1d1f;
+    box-sizing: border-box;
 
     &::placeholder {
-      color: #8e8e93;
+      color: rgba(60, 60, 67, 0.6);
     }
 
     &:focus {
-      border-color: #007AFF;
-      background: linear-gradient(135deg, #ffffff 0%, #f8f9fa 100%);
-      box-shadow: 0 0 0 4px rgba(0, 122, 255, 0.15), inset 0 2px 4px rgba(0, 0, 0, 0.05);
+      background: rgba(255, 255, 255, 0.9);
+      box-shadow: 0 0 0 1px rgba(0, 122, 255, 0.4);
     }
   }
 
   svg {
     position: absolute;
-    right: 16px;
+    left: 14px;
     top: 50%;
     transform: translateY(-50%);
-    color: #8e8e93;
+    color: rgba(60, 60, 67, 0.6);
     pointer-events: none;
+    width: 16px;
+    height: 16px;
   }
 }
 
-  .search-container {
-    margin-bottom: 20px;
-
-    .search-input-wrapper {
-      position: relative;
-      max-width: 500px;
-
-      .search-icon {
-        position: absolute;
-        left: 16px;
-        top: 50%;
-        transform: translateY(-50%);
-        color: #94a3b8;
-        z-index: 2;
-      }
-
-      .search-input {
-        width: 100%;
-        padding: 16px 16px 16px 48px;
-        border: 1px solid rgba(0, 0, 0, 0.1);
-        border-radius: 16px;
-        font-size: 16px;
-        background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
-        transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
-        outline: none;
-        box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.05), 0 1px 2px rgba(255, 255, 255, 0.8);
-
-        &::placeholder {
-          color: #8e8e93;
-        }
-
-        &:focus {
-          border-color: #007AFF;
-          background: linear-gradient(135deg, #ffffff 0%, #f8f9fa 100%);
-          box-shadow: 0 0 0 4px rgba(0, 122, 255, 0.15), inset 0 2px 4px rgba(0, 0, 0, 0.05);
-        }
-      }
-
-      .clear-search {
-        position: absolute;
-        right: 12px;
-        top: 50%;
-        transform: translateY(-50%);
-        width: 24px;
-        height: 24px;
-        border: none;
-        background: #e2e8f0;
-        border-radius: 50%;
-        color: #64748b;
-        cursor: pointer;
-        display: flex;
-        align-items: center;
-        justify-content: center;
-        transition: all 0.2s ease;
-
-        &:hover {
-          background: #cbd5e1;
-          color: #475569;
-        }
-      }
-    }
-  }
-
-  .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;
-
-  .loading-state {
-    display: flex;
-    flex-direction: column;
-    align-items: center;
-    justify-content: center;
-    padding: 80px 20px;
-    color: #8e8e93;
-
-    .loading-spinner {
-      width: 40px;
-      height: 40px;
-      border: 3px solid #e2e8f0;
-      border-top: 3px solid #3b82f6;
-      border-radius: 50%;
-      animation: spin 1s linear infinite;
-      margin-bottom: 16px;
-    }
-
-    p {
-      font-size: 18px;
-      margin: 0;
-      font-weight: 500;
-    }
-  }
-
-  .empty-state {
-    display: flex;
-    flex-direction: column;
-    align-items: center;
-    justify-content: center;
-    padding: 80px 20px;
-    text-align: center;
-    color: #8e8e93;
-
-    svg {
-      margin-bottom: 24px;
-      opacity: 0.6;
-      width: 80px;
-      height: 80px;
-    }
-
-    h3 {
-      font-size: 20px;
-      font-weight: 700;
-      color: #1d1d1f;
-      margin: 0 0 12px;
-      letter-spacing: -0.3px;
-    }
-
-    p {
-      font-size: 16px;
-      color: #8e8e93;
-      margin: 0;
-      font-weight: 500;
-    }
-  }
-
-  .consultation-list {
-    display: flex;
-    flex-direction: column;
-    gap: 16px;
-    margin-bottom: 24px;
-  }
-
-  // iOS咨询卡片
-  .consultation-card {
-    background: linear-gradient(135deg, #ffffff 0%, #f8f9fa 100%);
-    border: 1px solid rgba(0, 0, 0, 0.05);
-    border-radius: 20px;
-    padding: 24px;
-    transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
-    box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08), inset 0 1px 2px rgba(255, 255, 255, 0.8);
-    backdrop-filter: blur(20px);
-    -webkit-backdrop-filter: blur(20px);
-
-    &:hover {
-      transform: translateY(-2px);
-      box-shadow: 0 8px 30px rgba(0, 0, 0, 0.12), inset 0 1px 2px rgba(255, 255, 255, 0.8);
-    }
-
-    &.urgent {
-      border: 2px solid #FF3B30;
-      background: linear-gradient(135deg, #ffffff 0%, #fff5f5 100%);
-    }
-
-    .card-header {
-      display: flex;
-      align-items: center;
-      justify-content: space-between;
-      margin-bottom: 16px;
-
-      .customer-info {
-        display: flex;
-        align-items: center;
-        gap: 12px;
-
-        .avatar {
-          width: 48px;
-          height: 48px;
-          border-radius: 16px;
-          display: flex;
-          align-items: center;
-          justify-content: center;
-          font-weight: 700;
-          font-size: 18px;
-          color: white;
-          box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
-        }
-
-        h3 {
-          font-size: 18px;
-          font-weight: 600;
-          color: #1d1d1f;
-          margin: 0 0 4px;
-        }
-
-        .time {
-          font-size: 14px;
-          color: #8e8e93;
-          margin: 0;
-          font-weight: 500;
-        }
-      }
-
-      .status-badge {
-        padding: 8px 12px;
-        border-radius: 12px;
-        font-size: 12px;
-        font-weight: 600;
-        background: linear-gradient(135deg, #e5f4ff 0%, #cce7ff 100%);
-        color: #007AFF;
-        border: 1px solid rgba(0, 122, 255, 0.2);
-
-        &.urgent {
-          background: linear-gradient(135deg, #ffe5e5 0%, #ffcccc 100%);
-          color: #FF3B30;
-          border: 1px solid rgba(255, 59, 48, 0.2);
-        }
-      }
-    }
-
-    .content {
-      font-size: 16px;
-      color: #3c3c43;
-      line-height: 1.6;
-      margin: 0 0 20px;
-      font-weight: 500;
-    }
-
-    .card-footer {
-      display: flex;
-      gap: 12px;
-
-      .ios-btn {
-        flex: 1;
-        padding: 12px 20px;
-        border: none;
-        border-radius: 12px;
-        font-size: 14px;
-        font-weight: 600;
-        cursor: pointer;
-        transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
-        display: flex;
-        align-items: center;
-        justify-content: center;
-        gap: 8px;
-
-        &.primary {
-          background: linear-gradient(135deg, #007AFF 0%, #0062CC 100%);
-          color: white;
-          box-shadow: 0 2px 8px rgba(0, 122, 255, 0.3);
-
-          &:hover {
-            transform: translateY(-1px);
-            box-shadow: 0 4px 16px rgba(0, 122, 255, 0.4);
-          }
-        }
-
-        &.secondary {
-          background: linear-gradient(135deg, #f2f2f7 0%, #e5e5ea 100%);
-          color: #8e8e93;
-          border: 1px solid rgba(0, 0, 0, 0.1);
-
-          &:hover {
-            background: linear-gradient(135deg, #e5e5ea 0%, #d1d1d6 100%);
-            transform: translateY(-1px);
-          }
-        }
-      }
-    }
-  }
+// 咨询列表容器
+.consultation-list {
+  display: flex;
+  flex-direction: column;
+  gap: 12px;
 }
 
-// 咨询条目
-.consultation-item {
-  background: linear-gradient(135deg, #ffffff 0%, #f8f9fa 100%);
+// iOS风格横条卡片
+.consultation-card {
+  background: rgba(255, 255, 255, 0.9);
+  backdrop-filter: blur(20px);
+  -webkit-backdrop-filter: blur(20px);
+  border-radius: 16px;
+  padding: 16px;
   border: 1px solid rgba(0, 0, 0, 0.05);
-  border-radius: 20px;
-  padding: 24px;
   transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
   cursor: pointer;
-  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08), inset 0 1px 2px rgba(255, 255, 255, 0.8);
-  backdrop-filter: blur(10px);
-  -webkit-backdrop-filter: blur(10px);
   position: relative;
   overflow: hidden;
 
   &:hover {
-    box-shadow: 0 8px 32px rgba(0, 0, 0, 0.12), inset 0 1px 2px rgba(255, 255, 255, 0.8);
-    transform: translateY(-4px);
+    transform: translateY(-2px);
+    box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
+    border-color: rgba(0, 122, 255, 0.2);
   }
 
-  &.urgent {
-    border-left: 4px solid #FF3B30;
-    background: linear-gradient(135deg, #ffebee 0%, #ffffff 100%);
+  &:active {
+    transform: translateY(0);
+    box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
   }
 
-  &.sensitive {
-    border-left: 4px solid #FF9500;
-    background: linear-gradient(135deg, #fff3e0 0%, #ffffff 100%);
-  }
+  // 紧急状态样式
+  &.urgent {
+    border-left: 4px solid #FF3B30;
+    background: linear-gradient(135deg, rgba(255, 59, 48, 0.05) 0%, rgba(255, 255, 255, 0.9) 100%);
 
-  &.overdue {
-    border-left: 4px solid #FF2D55;
-    background: linear-gradient(135deg, #ffebee 0%, #ffffff 100%);
+    .status-badge.urgent {
+      background: linear-gradient(135deg, #FF3B30 0%, #FF6B6B 100%);
+      color: white;
+    }
   }
 
-  .item-header {
+  // 卡片头部
+  .card-header {
     display: flex;
-    align-items: flex-start;
+    align-items: center;
     justify-content: space-between;
-    margin-bottom: 16px;
+    margin-bottom: 12px;
 
-    .customer-section {
+    .customer-info {
       display: flex;
       align-items: center;
       gap: 12px;
       flex: 1;
 
-      .customer-avatar {
-        width: 52px;
-        height: 52px;
-        border-radius: 16px;
+      .avatar {
+        width: 44px;
+        height: 44px;
+        border-radius: 22px;
         display: flex;
         align-items: center;
         justify-content: center;
-        color: #ffffff;
-        font-size: 20px;
-        font-weight: 700;
-        flex-shrink: 0;
+        font-weight: 600;
+        font-size: 16px;
+        color: white;
         background: linear-gradient(135deg, #007AFF 0%, #5856D6 100%);
-        box-shadow: 0 4px 12px rgba(0, 122, 255, 0.3);
+        box-shadow: 0 2px 8px rgba(0, 122, 255, 0.3);
       }
 
-      .customer-details {
+      div {
         flex: 1;
+        min-width: 0;
 
-        .customer-name {
-          font-size: 20px;
-          font-weight: 700;
+        h3 {
+          font-size: 17px;
+          font-weight: 600;
           color: #1d1d1f;
-          margin: 0 0 6px;
+          margin: 0 0 2px 0;
           line-height: 1.3;
-          letter-spacing: -0.3px;
         }
 
-        .customer-meta {
-          display: flex;
-          align-items: center;
-          gap: 8px;
-          font-size: 14px;
-          color: #8e8e93;
+        .time {
+          font-size: 13px;
+          color: rgba(60, 60, 67, 0.6);
+          margin: 0;
           font-weight: 500;
-
-          .separator {
-            color: #c7c7cc;
-          }
-
-          .reply-status {
-            &.overdue {
-              color: #FF3B30;
-              font-weight: 600;
-            }
-          }
         }
       }
     }
 
-    .priority-badges {
-      display: flex;
-      gap: 8px;
-      flex-shrink: 0;
-
-      .priority-badge {
-        display: flex;
-        align-items: center;
-        gap: 4px;
-        padding: 8px 12px;
-        border-radius: 14px;
-        font-size: 12px;
-        font-weight: 600;
-        text-transform: uppercase;
-        letter-spacing: 0.5px;
-        backdrop-filter: blur(10px);
-        -webkit-backdrop-filter: blur(10px);
-
-        &.urgent {
-          background: rgba(255, 59, 48, 0.1);
-          color: #FF3B30;
-          border: 1px solid rgba(255, 59, 48, 0.2);
-        }
-
-        &.sensitive {
-          background: rgba(255, 149, 0, 0.1);
-          color: #FF9500;
-          border: 1px solid rgba(255, 149, 0, 0.2);
-        }
+    .status-badge {
+      padding: 6px 12px;
+      border-radius: 20px;
+      font-size: 12px;
+      font-weight: 600;
+      background: rgba(120, 120, 128, 0.12);
+      color: rgba(60, 60, 67, 0.8);
+      white-space: nowrap;
+
+      &.urgent {
+        background: linear-gradient(135deg, #FF3B30 0%, #FF6B6B 100%);
+        color: white;
+        box-shadow: 0 2px 8px rgba(255, 59, 48, 0.3);
       }
     }
   }
 
-  .consultation-content-text {
-    margin-bottom: 24px;
-
-    p {
-      font-size: 16px;
-      line-height: 1.7;
-      color: #3c3c43;
-      margin: 0;
-      font-weight: 500;
-
-      :global(mark) {
-        background: rgba(255, 149, 0, 0.15);
-        color: #FF9500;
-        padding: 2px 6px;
-        border-radius: 6px;
-        font-weight: 600;
-      }
-    }
+  // 咨询内容
+  .content {
+    font-size: 15px;
+    color: rgba(60, 60, 67, 0.8);
+    line-height: 1.4;
+    margin: 0 0 16px 0;
+    display: -webkit-box;
+    -webkit-line-clamp: 2;
+    -webkit-box-orient: vertical;
+    overflow: hidden;
+    font-weight: 400;
   }
 
-  .item-actions {
+  // 卡片底部操作按钮
+  .card-footer {
     display: flex;
-    gap: 12px;
-    flex-wrap: wrap;
+    gap: 8px;
+    align-items: center;
 
-    .action-btn {
-      display: flex;
-      align-items: center;
-      gap: 8px;
-      padding: 12px 18px;
-      border-radius: 12px;
+    .ios-btn {
+      padding: 8px 16px;
+      border-radius: 20px;
       font-size: 14px;
       font-weight: 600;
-      cursor: pointer;
-      transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
       border: none;
-      outline: none;
-      box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
-
-      &:disabled {
-        opacity: 0.6;
-        cursor: not-allowed;
-      }
+      cursor: pointer;
+      transition: all 0.2s ease;
+      flex: 1;
+      text-align: center;
 
       &.primary {
         background: linear-gradient(135deg, #007AFF 0%, #5856D6 100%);
-        color: #ffffff;
+        color: white;
+        box-shadow: 0 2px 8px rgba(0, 122, 255, 0.3);
 
-        &:hover:not(:disabled) {
-          background: linear-gradient(135deg, #0062CC 0%, #4A4AB8 100%);
-          transform: translateY(-2px);
-          box-shadow: 0 6px 20px rgba(0, 122, 255, 0.4);
+        &:hover {
+          transform: translateY(-1px);
+          box-shadow: 0 4px 12px rgba(0, 122, 255, 0.4);
+        }
+
+        &:active {
+          transform: translateY(0);
+          box-shadow: 0 2px 6px rgba(0, 122, 255, 0.3);
         }
       }
 
       &.secondary {
-        background: linear-gradient(135deg, #ffffff 0%, #f8f9fa 100%);
-        color: #6c757d;
-        border: 1px solid rgba(0, 0, 0, 0.08);
+        background: rgba(120, 120, 128, 0.12);
+        color: #007AFF;
 
         &:hover {
-          background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
-          border-color: rgba(0, 0, 0, 0.15);
+          background: rgba(120, 120, 128, 0.16);
           transform: translateY(-1px);
-          box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
         }
-      }
-
-      &.tertiary {
-        background: linear-gradient(135deg, #34C759 0%, #30B855 100%);
-        color: #ffffff;
-        border: none;
 
-        &:hover {
-          background: linear-gradient(135deg, #30B855 0%, #2DA54A 100%);
-          transform: translateY(-2px);
-          box-shadow: 0 6px 20px rgba(52, 199, 89, 0.4);
+        &:active {
+          transform: translateY(0);
+          background: rgba(120, 120, 128, 0.2);
         }
       }
     }
   }
 }
 
-// 动画
-@keyframes spin {
-  from {
-    transform: rotate(0deg);
+// 空状态样式
+.empty-state {
+  text-align: center;
+  padding: 60px 20px;
+  color: rgba(60, 60, 67, 0.6);
+
+  svg {
+    margin-bottom: 16px;
+    opacity: 0.5;
   }
-  to {
-    transform: rotate(360deg);
+
+  h3 {
+    font-size: 20px;
+    font-weight: 600;
+    color: rgba(60, 60, 67, 0.8);
+    margin: 0 0 8px 0;
+  }
+
+  p {
+    font-size: 15px;
+    color: rgba(60, 60, 67, 0.6);
+    margin: 0;
+    font-weight: 400;
   }
 }
 
 // 响应式设计
 @media (max-width: 768px) {
-  .page-header {
-    padding: 16px 20px;
-
-    .header-title h1 {
-      font-size: 20px;
-    }
-
-    .header-actions .refresh-btn {
-      padding: 8px 12px;
-      font-size: 13px;
-    }
+  .ios-content {
+    padding: 16px;
   }
 
-  .search-filter-section {
-    padding: 20px;
+  .consultation-card {
+    .card-header {
+      .customer-info {
+        .avatar {
+          width: 40px;
+          height: 40px;
+          border-radius: 20px;
+          font-size: 14px;
+        }
 
-    .search-container .search-input-wrapper .search-input {
-      font-size: 16px; // 防止iOS缩放
+        div h3 {
+          font-size: 16px;
+        }
+      }
     }
 
-    .filter-chips {
+    .card-footer {
+      flex-direction: column;
       gap: 8px;
 
-      .filter-chip {
-        padding: 8px 12px;
-        font-size: 13px;
+      .ios-btn {
+        width: 100%;
       }
     }
   }
+}
 
-  .consultation-content {
-    padding: 20px;
-
-    .consultation-item {
-      padding: 16px;
-
-      .item-header {
-        .customer-section {
-          .customer-avatar {
-            width: 40px;
-            height: 40px;
-            font-size: 16px;
-          }
-
-          .customer-details .customer-name {
-            font-size: 16px;
-          }
-        }
-      }
-
-      .consultation-content-text p {
-        font-size: 15px;
-      }
-
-      .item-actions {
-        gap: 8px;
-
-        .action-btn {
-          padding: 8px 12px;
-          font-size: 13px;
-        }
-      }
-    }
+// 动画效果
+@keyframes slideInUp {
+  from {
+    opacity: 0;
+    transform: translateY(20px);
+  }
+  to {
+    opacity: 1;
+    transform: translateY(0);
   }
 }
 
-@media (max-width: 480px) {
-  .page-header {
-    .header-left {
-      gap: 12px;
-    }
+.consultation-card {
+  animation: slideInUp 0.3s ease-out;
+}
 
-    .header-title h1 {
-      font-size: 18px;
-    }
+// 加载动画
+@keyframes pulse {
+  0%, 100% {
+    opacity: 1;
   }
+  50% {
+    opacity: 0.5;
+  }
+}
 
-  .consultation-item {
-    .item-header {
-      flex-direction: column;
-      align-items: flex-start;
-      gap: 12px;
-
-      .priority-badges {
-        align-self: flex-end;
-      }
-    }
-
-    .item-actions {
-      .action-btn {
-        flex: 1;
-        justify-content: center;
-        min-width: 0;
-      }
-    }
+.loading {
+  .consultation-card {
+    animation: pulse 1.5s ease-in-out infinite;
   }
 }

+ 94 - 391
src/app/pages/customer-service/dashboard/pages/consultation-list/consultation-list.component.ts

@@ -1,453 +1,156 @@
 import { Component, OnInit } from '@angular/core';
 import { CommonModule } from '@angular/common';
-import { RouterModule, ActivatedRoute } from '@angular/router';
+import { RouterModule, Router } from '@angular/router';
 import { FormsModule } from '@angular/forms';
 
 @Component({
   selector: 'app-consultation-list',
   standalone: true,
   imports: [CommonModule, RouterModule, FormsModule],
-  template: `
-    <div class="consultation-container">
-      <!-- 页面头部 -->
-      <header class="page-header">
-        <div class="header-left">
-          <button class="back-btn" (click)="goBack()">
-            <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
-              <path d="M19 12H5M12 19l-7-7 7-7"/>
-            </svg>
-          </button>
-          <div class="header-title">
-            <h1>客户咨询记录</h1>
-            <p class="subtitle">共 {{ filteredConsultations.length }} 条咨询记录</p>
-          </div>
-        </div>
-        <div class="header-actions">
-          <button class="refresh-btn" (click)="refreshData()" [disabled]="isLoading">
-            <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" [class.spinning]="isLoading">
-              <polyline points="23 4 23 10 17 10"></polyline>
-              <polyline points="1 20 1 14 7 14"></polyline>
-              <path d="m20.49 9A9 9 0 0 0 5.64 5.64L1 10m22 4l-4.64 4.36A9 9 0 0 1 3.51 15"></path>
-            </svg>
-            刷新
-          </button>
-        </div>
-      </header>
-
-      <!-- 搜索和筛选区域 -->
-      <div class="search-filter-section">
-        <div class="search-container">
-          <div class="search-input-wrapper">
-            <svg class="search-icon" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
-              <circle cx="11" cy="11" r="8"></circle>
-              <path d="m21 21-4.35-4.35"></path>
-            </svg>
-            <input 
-              type="text" 
-              placeholder="搜索客户姓名、咨询内容..." 
-              [(ngModel)]="searchKeyword" 
-              (input)="applyFilters()"
-              class="search-input"
-            >
-            <button *ngIf="searchKeyword" class="clear-search" (click)="clearSearch()">
-              <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
-                <line x1="18" y1="6" x2="6" y2="18"></line>
-                <line x1="6" y1="6" x2="18" y2="18"></line>
-              </svg>
-            </button>
-          </div>
-        </div>
-        
-        <div class="filter-chips">
-          <button 
-            class="filter-chip" 
-            [class.active]="filterUnrepliedOver1h" 
-            (click)="toggleFilter('unreplied')"
-          >
-            <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>
-            未回复超1小时
-            <span *ngIf="filterUnrepliedOver1h" class="chip-count">{{ getUnrepliedCount() }}</span>
-          </button>
-          <button 
-            class="filter-chip" 
-            [class.active]="filterSensitive" 
-            (click)="toggleFilter('sensitive')"
-          >
-            <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
-              <path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"></path>
-            </svg>
-            含敏感词
-            <span *ngIf="filterSensitive" class="chip-count">{{ getSensitiveCount() }}</span>
-          </button>
-          <button 
-            class="filter-chip" 
-            [class.active]="filterHighPriority" 
-            (click)="toggleFilter('priority')"
-          >
-            <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
-              <path d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126zM12 15.75h.007v.008H12v-.008z"></path>
-            </svg>
-            紧急咨询
-            <span *ngIf="filterHighPriority" class="chip-count">{{ getHighPriorityCount() }}</span>
-          </button>
-        </div>
-      </div>
-
-      <!-- 咨询列表 -->
-      <div class="consultation-content">
-        <div *ngIf="isLoading" class="loading-state">
-          <div class="loading-spinner"></div>
-          <p>加载中...</p>
-        </div>
-
-        <div *ngIf="!isLoading && filteredConsultations.length === 0" class="empty-state">
-          <svg width="64" height="64" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1">
-            <circle cx="11" cy="11" r="8"></circle>
-            <path d="m21 21-4.35-4.35"></path>
-          </svg>
-          <h3>暂无咨询记录</h3>
-          <p>{{ searchKeyword ? '没有找到匹配的咨询记录' : '还没有客户咨询记录' }}</p>
-        </div>
-
-        <div *ngIf="!isLoading && filteredConsultations.length > 0" class="consultation-list">
-          <div 
-            class="consultation-item" 
-            *ngFor="let item of filteredConsultations; trackBy: trackByConsultation"
-            [class.urgent]="item.priority === 'high'"
-            [class.sensitive]="containsSensitiveWords(item.content)"
-            [class.overdue]="isOverdue(item)"
-          >
-            <div class="item-header">
-              <div class="customer-section">
-                <div class="customer-avatar" [style.background-color]="getAvatarColor(item.customer)">
-                  {{ item.customer.charAt(0) }}
-                </div>
-                <div class="customer-details">
-                  <h3 class="customer-name">{{ item.customer }}</h3>
-                  <div class="customer-meta">
-                    <span class="consultation-time">{{ formatTime(item.time) }}</span>
-                    <span class="separator">•</span>
-                    <span class="reply-status" [class.overdue]="isOverdue(item)">
-                      {{ getReplyStatus(item) }}
-                    </span>
-                  </div>
-                </div>
-              </div>
-              <div class="priority-badges">
-                <span *ngIf="item.priority === 'high'" class="priority-badge urgent">
-                  <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
-                    <path d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126zM12 15.75h.007v.008H12v-.008z"></path>
-                  </svg>
-                  紧急
-                </span>
-                <span *ngIf="containsSensitiveWords(item.content)" class="priority-badge sensitive">
-                  <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
-                    <path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"></path>
-                  </svg>
-                  敏感词
-                </span>
-              </div>
-            </div>
-            
-            <div class="consultation-content-text">
-              <p [innerHTML]="highlightKeyword(item.content)"></p>
-            </div>
-            
-            <div class="item-actions">
-              <button 
-                class="action-btn primary" 
-                (click)="syncToWeChat(item)"
-                [disabled]="item.syncing"
-              >
-                <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
-                  <path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"></path>
-                </svg>
-                <span *ngIf="!item.syncing">同步到企微</span>
-                <span *ngIf="item.syncing">同步中...</span>
-              </button>
-              <button class="action-btn secondary" (click)="viewDetails(item)">
-                <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
-                  <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="action-btn tertiary" (click)="markAsReplied(item)">
-                <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
-                  <polyline points="20 6 9 17 4 12"></polyline>
-                </svg>
-                标记已回复
-              </button>
-            </div>
-          </div>
-        </div>
-      </div>
-    </div>
-  `,
-  styles: [`
-    .toolbar { display:flex; justify-content: space-between; align-items:center; gap:12px; margin-bottom: 12px; }
-    .quick-filters { display:flex; gap: 16px; color:#636366; font-size:14px; }
-    .quick-filters input { margin-right:6px; }
-    .search-bar { position: relative; }
-    .search-bar input { width: 260px; padding: 10px 14px 10px 36px; border-radius: 10px; border: 1px solid #d1d1d6; background: #f7f7fb; }
-    .search-bar svg { position:absolute; left: 10px; top: 50%; transform: translateY(-50%); color:#8e8e93; }
-  `]
+  templateUrl: './consultation-list.component.html',
+  styleUrls: ['./consultation-list.component.scss']
 })
 export class ConsultationListComponent implements OnInit {
+  // 咨询数据
   consultations = [
     {
       id: '1',
-      customer: '张先生', 
-      time: '10:30', 
-      content: '咨询关于厨房改造的预算和工期,希望能在下个月开始施工', 
-      priority: 'normal', 
-      lastReplyMinutes: 30, 
-      weComUserId: 'zhangxiansheng',
-      syncing: false,
-      replied: false
+      customerName: '张先生',
+      content: '咨询关于厨房改造的预算和工期,希望能在下个月开始施工,请尽快联系我',
+      priority: 'normal',
+      createdAt: new Date('2024-01-15T10:30:00'),
+      status: 'pending'
     },
     {
       id: '2',
-      customer: '李女士', 
-      time: '11:45', 
-      content: '询问客厅设计风格建议,发手机给你,希望尽快联系', 
-      priority: 'high', 
-      lastReplyMinutes: 120, 
-      weComUserId: 'linnvshi',
-      syncing: false,
-      replied: false
+      customerName: '李女士',
+      content: '询问客厅设计风格建议,希望能提供一些现代简约风格的方案参考',
+      priority: 'urgent',
+      createdAt: new Date('2024-01-15T11:45:00'),
+      status: 'pending'
     },
     {
       id: '3',
-      customer: '王先生', 
-      time: '14:20', 
-      content: '需要全屋设计方案咨询,发邮箱吧,预算在20万左右', 
-      priority: 'normal', 
-      lastReplyMinutes: 75, 
-      weComUserId: 'wangxiansheng',
-      syncing: false,
-      replied: false
+      customerName: '王先生',
+      content: '需要全屋设计方案咨询,预算在20万左右,希望能安排设计师上门测量',
+      priority: 'normal',
+      createdAt: new Date('2024-01-15T14:20:00'),
+      status: 'pending'
     },
     {
       id: '4',
-      customer: '陈女士', 
-      time: '09:15', 
-      content: '想了解卧室装修的具体流程和注意事项', 
-      priority: 'normal', 
-      lastReplyMinutes: 15, 
-      weComUserId: 'chennvshi',
-      syncing: false,
-      replied: true
+      customerName: '陈女士',
+      content: '想了解卧室装修的具体流程和注意事项,特别是环保材料的选择',
+      priority: 'normal',
+      createdAt: new Date('2024-01-15T09:15:00'),
+      status: 'completed'
     },
     {
       id: '5',
-      customer: '刘先生', 
-      time: '16:30', 
-      content: '紧急!明天要看房,需要设计师现场指导,找不到人联系', 
-      priority: 'high', 
-      lastReplyMinutes: 180, 
-      weComUserId: 'liuxiansheng',
-      syncing: false,
-      replied: false
+      customerName: '刘先生',
+      content: '紧急!明天要看房,需要设计师现场指导,请务必今天联系我',
+      priority: 'urgent',
+      createdAt: new Date('2024-01-15T16:30:00'),
+      status: 'pending'
     }
   ];
 
   filteredConsultations = [...this.consultations];
-  searchKeyword = '';
-  filterUnrepliedOver1h = false;
-  filterSensitive = false;
-  filterHighPriority = false;
-  isLoading = false;
-
-  private sensitivePatterns = /(发手机|发邮箱|找不到人|微信|QQ|电话|联系方式)/;
+  searchTerm = '';
 
-  constructor(private route: ActivatedRoute) {}
+  constructor(private router: Router) {}
 
   ngOnInit(): void {
-    this.route.queryParams.subscribe(params => {
-      const q = params['q'];
-      if (q) {
-        this.searchKeyword = q;
-      }
-      this.applyFilters();
-    });
+    this.onSearch();
   }
 
-  // 搜索过滤功能
-  filterConsultations() {
-    if (!this.searchKeyword.trim()) {
+  // 搜索功能
+  onSearch(): void {
+    if (!this.searchTerm.trim()) {
       this.filteredConsultations = [...this.consultations];
       return;
     }
 
-    const keyword = this.searchKeyword.toLowerCase();
-    this.filteredConsultations = this.consultations.filter(item => 
-      item.customer.toLowerCase().includes(keyword) ||
-      item.content.toLowerCase().includes(keyword)
+    this.filteredConsultations = this.consultations.filter(consultation =>
+      consultation.customerName.toLowerCase().includes(this.searchTerm.toLowerCase()) ||
+      consultation.content.toLowerCase().includes(this.searchTerm.toLowerCase())
     );
   }
 
-  applyFilters(): void {
-    this.filteredConsultations = this.consultations.filter(item => {
-      const matchesKeyword = !this.searchKeyword || 
-        item.customer.toLowerCase().includes(this.searchKeyword.toLowerCase()) || 
-        item.content.toLowerCase().includes(this.searchKeyword.toLowerCase());
-      const matchesUnreplied = !this.filterUnrepliedOver1h || item.lastReplyMinutes > 60;
-      const matchesSensitive = !this.filterSensitive || this.containsSensitiveWords(item.content);
-      const matchesPriority = !this.filterHighPriority || item.priority === 'high';
-      return matchesKeyword && matchesUnreplied && matchesSensitive && matchesPriority;
-    });
+  // 查看咨询详情
+  viewConsultation(consultation: any): void {
+    console.log('查看咨询:', consultation);
+    // 这里可以导航到详情页面
   }
 
-  toggleFilter(type: string): void {
-    switch (type) {
-      case 'unreplied':
-        this.filterUnrepliedOver1h = !this.filterUnrepliedOver1h;
-        break;
-      case 'sensitive':
-        this.filterSensitive = !this.filterSensitive;
-        break;
-      case 'priority':
-        this.filterHighPriority = !this.filterHighPriority;
-        break;
-    }
-    this.applyFilters();
-  }
-
-  clearSearch(): void {
-    this.searchKeyword = '';
-    this.applyFilters();
+  // 处理咨询
+  handleConsultation(consultation: any, event: Event): void {
+    event.stopPropagation();
+    console.log('处理咨询:', consultation);
   }
 
-  refreshData(): void {
-    this.isLoading = true;
-    // 模拟数据刷新
-    setTimeout(() => {
-      this.isLoading = false;
-      this.applyFilters();
-    }, 1000);
+  // 查看详情
+  viewDetails(consultation: any, event: Event): void {
+    event.stopPropagation();
+    console.log('查看详情:', consultation);
   }
 
-  containsSensitiveWords(content: string): boolean {
-    return this.sensitivePatterns.test(content);
+  // 返回上一页
+  goBack(): void {
+    this.router.navigate(['/customer-service']);
   }
 
-  isOverdue(item: any): boolean {
-    return item.lastReplyMinutes > 60 && !item.replied;
+  // 获取优先级标签
+  getPriorityLabel(priority: string): string {
+    switch (priority) {
+      case 'urgent':
+        return '紧急';
+      case 'high':
+        return '高';
+      case 'normal':
+        return '普通';
+      case 'low':
+        return '低';
+      default:
+        return '普通';
+    }
   }
 
-  getReplyStatus(item: any): string {
-    if (item.replied) {
-      return '已回复';
+  // 获取状态标签
+  getStatusLabel(status: string): string {
+    switch (status) {
+      case 'pending':
+        return '待处理';
+      case 'processing':
+        return '处理中';
+      case 'completed':
+        return '已完成';
+      default:
+        return '待处理';
     }
-    if (item.lastReplyMinutes > 60) {
-      return `超时 ${Math.floor(item.lastReplyMinutes / 60)} 小时`;
-    }
-    return `${item.lastReplyMinutes} 分钟前`;
   }
 
-  formatTime(time: string): string {
-    return `今天 ${time}`;
+  // 格式化时间
+  formatTime(date: Date): string {
+    const now = new Date();
+    const diff = now.getTime() - date.getTime();
+    const minutes = Math.floor(diff / (1000 * 60));
+    const hours = Math.floor(diff / (1000 * 60 * 60));
+    const days = Math.floor(diff / (1000 * 60 * 60 * 24));
+
+    if (minutes < 60) {
+      return `${minutes}分钟前`;
+    } else if (hours < 24) {
+      return `${hours}小时前`;
+    } else {
+      return `${days}天前`;
+    }
   }
 
+  // 获取头像颜色
   getAvatarColor(name: string): string {
     const colors = ['#007AFF', '#34C759', '#FF9500', '#FF3B30', '#AF52DE', '#5AC8FA'];
     const index = name.charCodeAt(0) % colors.length;
     return colors[index];
   }
-
-  highlightKeyword(content: string): string {
-    if (!this.searchKeyword) return content;
-    const regex = new RegExp(`(${this.searchKeyword})`, 'gi');
-    return content.replace(regex, '<mark>$1</mark>');
-  }
-
-  trackByConsultation(index: number, item: any): string {
-    return item.id;
-  }
-
-  getUnrepliedCount(): number {
-    return this.consultations.filter(item => item.lastReplyMinutes > 60 && !item.replied).length;
-  }
-
-  getSensitiveCount(): number {
-    return this.consultations.filter(item => this.containsSensitiveWords(item.content)).length;
-  }
-
-  getHighPriorityCount(): number {
-    return this.consultations.filter(item => item.priority === 'high').length;
-  }
-
-  syncToWeChat(item: any): void {
-    item.syncing = true;
-    
-    try {
-      // 尝试打开企业微信应用
-      const wecomUrl = `wxwork://message/?username=${item.weComUserId}`;
-      window.open(wecomUrl, '_blank');
-      
-      // 模拟同步过程
-      setTimeout(() => {
-        item.syncing = false;
-        // 显示成功提示
-        this.showToast('已成功同步到企业微信');
-      }, 2000);
-      
-      // 备选方案:打开企业微信网页版
-      setTimeout(() => {
-        const webUrl = `https://work.weixin.qq.com/wework_admin/frame#index`;
-        window.open(webUrl, '_blank');
-      }, 1000);
-    } catch (error) {
-      console.error('同步到企业微信失败:', error);
-      item.syncing = false;
-      this.showToast('同步失败,请检查企业微信客户端', 'error');
-    }
-  }
-
-  viewDetails(item: any): void {
-    // 这里可以导航到详情页面或打开模态框
-    console.log('查看详情:', item);
-    this.showToast(`正在查看 ${item.customer} 的咨询详情`);
-  }
-
-  markAsReplied(item: any): void {
-    item.replied = true;
-    this.showToast(`已标记 ${item.customer} 的咨询为已回复`);
-    this.applyFilters();
-  }
-
-  private showToast(message: string, type: 'success' | 'error' = 'success'): void {
-    // 简单的提示实现,实际项目中可以使用更完善的提示组件
-    const toast = document.createElement('div');
-    toast.textContent = message;
-    toast.style.cssText = `
-      position: fixed;
-      top: 20px;
-      right: 20px;
-      background: ${type === 'success' ? '#34C759' : '#FF3B30'};
-      color: white;
-      padding: 12px 20px;
-      border-radius: 8px;
-      z-index: 10000;
-      font-size: 14px;
-      box-shadow: 0 4px 12px rgba(0,0,0,0.15);
-    `;
-    document.body.appendChild(toast);
-    
-    setTimeout(() => {
-      document.body.removeChild(toast);
-    }, 3000);
-  }
-
-  openWeCom(item: any): void {
-    this.syncToWeChat(item);
-  }
-
-  goBack() {
-    window.history.back();
-  }
 }

+ 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 {

+ 0 - 170
src/app/pages/customer-service/project-detail/complaint-warning-dialog.ts

@@ -1,170 +0,0 @@
-import { Component, Inject } from '@angular/core';
-import { CommonModule } from '@angular/common';
-import { FormsModule } from '@angular/forms';
-import { MatButtonModule } from '@angular/material/button';
-import { MatDialogModule, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
-import { MatFormFieldModule } from '@angular/material/form-field';
-import { MatInputModule } from '@angular/material/input';
-import { MatSelectModule } from '@angular/material/select';
-import { MatIconModule } from '@angular/material/icon';
-
-interface ComplaintWarningData {
-  projectId: string;
-  projectName: string;
-}
-
-@Component({
-  selector: 'app-complaint-warning-dialog',
-  standalone: true,
-  imports: [
-    CommonModule,
-    FormsModule,
-    MatButtonModule,
-    MatDialogModule,
-    MatFormFieldModule,
-    MatInputModule,
-    MatSelectModule,
-    MatIconModule
-  ],
-  template: `
-    <h2 mat-dialog-title>投诉预警</h2>
-    <mat-dialog-content>
-      <div class="dialog-content">
-        <div class="project-info">
-          <p><strong>项目名称:</strong> {{ data.projectName }}</p>
-        </div>
-        
-        <div class="form-group">
-          <label for="complaint-level">预警等级 *</label>
-          <mat-form-field appearance="outline" class="form-field">
-            <mat-select [(ngModel)]="complaintLevel" id="complaint-level" name="complaintLevel" required>
-              <mat-option value="low">低级别</mat-option>
-              <mat-option value="medium">中级别</mat-option>
-              <mat-option value="high">高级别</mat-option>
-            </mat-select>
-          </mat-form-field>
-        </div>
-        
-        <div class="form-group">
-          <label for="complaint-content">投诉内容 *</label>
-          <mat-form-field appearance="outline" class="form-field">
-            <textarea 
-              matInput 
-              [(ngModel)]="complaintContent" 
-              id="complaint-content" 
-              name="complaintContent" 
-              rows="4" 
-              placeholder="请详细描述客户投诉的内容..."
-              required
-            ></textarea>
-          </mat-form-field>
-        </div>
-        
-        <div class="form-group">
-          <label for="potential-risk">潜在风险</label>
-          <mat-form-field appearance="outline" class="form-field">
-            <textarea 
-              matInput 
-              [(ngModel)]="potentialRisk" 
-              id="potential-risk" 
-              name="potential-risk" 
-              rows="2" 
-              placeholder="分析可能带来的影响和风险..."
-            ></textarea>
-          </mat-form-field>
-        </div>
-        
-        <div class="form-group">
-          <label for="suggested-action">建议措施</label>
-          <mat-form-field appearance="outline" class="form-field">
-            <textarea 
-              matInput 
-              [(ngModel)]="suggestedAction" 
-              id="suggested-action" 
-              name="suggested-action" 
-              rows="2" 
-              placeholder="建议采取的解决措施..."
-            ></textarea>
-          </mat-form-field>
-        </div>
-      </div>
-    </mat-dialog-content>
-    <mat-dialog-actions align="end">
-      <button mat-button (click)="onCancel()">取消</button>
-      <button mat-button color="primary" (click)="onSubmit()" [disabled]="!isFormValid()">
-        提交预警
-      </button>
-    </mat-dialog-actions>
-  `,
-  styles: [`
-    .dialog-content {
-      padding: 10px 0;
-    }
-    
-    .project-info {
-      padding: 12px;
-      background-color: #fff3e0;
-      border-radius: 8px;
-      margin-bottom: 16px;
-    }
-    
-    .form-group {
-      margin-bottom: 20px;
-    }
-    
-    label {
-      display: block;
-      margin-bottom: 6px;
-      font-weight: 500;
-      color: rgba(0, 0, 0, 0.87);
-    }
-    
-    p {
-      margin: 0;
-      color: rgba(0, 0, 0, 0.6);
-    }
-    
-    .form-field {
-      width: 100%;
-    }
-    
-    .mat-mdc-dialog-actions {
-      padding: 16px 24px;
-    }
-  `]
-}) 
-export class ComplaintWarningDialog {
-  complaintLevel: string = '';
-  complaintContent: string = '';
-  potentialRisk: string = '';
-  suggestedAction: string = '';
-
-  constructor(
-    public dialogRef: MatDialogRef<ComplaintWarningDialog>,
-    @Inject(MAT_DIALOG_DATA) public data: ComplaintWarningData
-  ) {}
-
-  onCancel(): void {
-    this.dialogRef.close();
-  }
-
-  isFormValid(): boolean {
-    return !!this.complaintLevel && !!this.complaintContent.trim();
-  }
-
-  onSubmit(): void {
-    if (!this.isFormValid()) return;
-    
-    const formData = {
-      projectId: this.data.projectId,
-      projectName: this.data.projectName,
-      complaintLevel: this.complaintLevel,
-      complaintContent: this.complaintContent,
-      potentialRisk: this.potentialRisk,
-      suggestedAction: this.suggestedAction,
-      reportedAt: new Date().toISOString()
-    };
-    
-    this.dialogRef.close({ confirmed: true, data: formData });
-  }
-}

+ 0 - 162
src/app/pages/customer-service/project-detail/modification-request-dialog.ts

@@ -1,162 +0,0 @@
-import { Component, Inject } from '@angular/core';
-import { CommonModule } from '@angular/common';
-import { FormsModule } from '@angular/forms';
-import { MatButtonModule } from '@angular/material/button';
-import { MatDialogModule, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
-import { MatFormFieldModule } from '@angular/material/form-field';
-import { MatInputModule } from '@angular/material/input';
-import { MatSelectModule } from '@angular/material/select';
-import { MatIconModule } from '@angular/material/icon';
-
-interface ModificationRequestData {
-  projectId: string;
-  projectName: string;
-  projectStatus?: string;
-}
-
-@Component({
-  selector: 'app-modification-request-dialog',
-  standalone: true,
-  imports: [
-    CommonModule,
-    FormsModule,
-    MatButtonModule,
-    MatDialogModule,
-    MatFormFieldModule,
-    MatInputModule,
-    MatSelectModule,
-    MatIconModule
-  ],
-  template: `
-    <h2 mat-dialog-title>申请修改</h2>
-    <mat-dialog-content>
-      <div class="dialog-content">
-        <div class="project-info">
-          <p><strong>项目名称:</strong> {{ data.projectName }}</p>
-        </div>
-        
-        <div class="form-group">
-          <label for="modification-type">修改类型 *</label>
-          <mat-form-field appearance="outline" class="form-field">
-            <mat-select [(ngModel)]="modificationType" id="modification-type" name="modificationType" required>
-              <mat-option value="content">内容修改</mat-option>
-              <mat-option value="design">设计修改</mat-option>
-              <mat-option value="time">时间调整</mat-option>
-              <mat-option value="other">其他</mat-option>
-            </mat-select>
-          </mat-form-field>
-        </div>
-        
-        <div class="form-group">
-          <label for="description">修改说明 *</label>
-          <mat-form-field appearance="outline" class="form-field">
-            <textarea 
-              matInput 
-              [(ngModel)]="description" 
-              id="description" 
-              name="description" 
-              rows="4" 
-              placeholder="请详细描述需要修改的内容..."
-              required
-            ></textarea>
-          </mat-form-field>
-        </div>
-        
-        <div class="form-group">
-          <label for="expected-time">期望完成时间</label>
-          <mat-form-field appearance="outline" class="form-field">
-            <input 
-              matInput 
-              [(ngModel)]="expectedTime" 
-              id="expected-time" 
-              name="expectedTime" 
-              type="text" 
-              placeholder="如:3天内"
-            >
-          </mat-form-field>
-        </div>
-        
-        <div class="form-group">
-          <label for="contact-info">联系人信息</label>
-          <p>{{ contactInfo }}</p>
-        </div>
-      </div>
-    </mat-dialog-content>
-    <mat-dialog-actions align="end">
-      <button mat-button (click)="onCancel()">取消</button>
-      <button mat-button color="primary" (click)="onSubmit()" [disabled]="!isFormValid()">
-        提交申请
-      </button>
-    </mat-dialog-actions>
-  `,
-  styles: [`
-    .dialog-content {
-      padding: 10px 0;
-    }
-    
-    .project-info {
-      padding: 12px;
-      background-color: #f5f5f5;
-      border-radius: 8px;
-      margin-bottom: 16px;
-    }
-    
-    .form-group {
-      margin-bottom: 20px;
-    }
-    
-    label {
-      display: block;
-      margin-bottom: 6px;
-      font-weight: 500;
-      color: rgba(0, 0, 0, 0.87);
-    }
-    
-    p {
-      margin: 0;
-      color: rgba(0, 0, 0, 0.6);
-    }
-    
-    .form-field {
-      width: 100%;
-    }
-    
-    .mat-mdc-dialog-actions {
-      padding: 16px 24px;
-    }
-  `]
-}) 
-export class ModificationRequestDialog {
-  modificationType: string = '';
-  description: string = '';
-  expectedTime: string = '';
-  contactInfo: string = '客服小李 (电话: 138****6789)';
-
-  constructor(
-    public dialogRef: MatDialogRef<ModificationRequestDialog>,
-    @Inject(MAT_DIALOG_DATA) public data: ModificationRequestData
-  ) {}
-
-  onCancel(): void {
-    this.dialogRef.close();
-  }
-
-  isFormValid(): boolean {
-    return !!this.modificationType && !!this.description.trim();
-  }
-
-  onSubmit(): void {
-    if (!this.isFormValid()) return;
-    
-    const formData = {
-      projectId: this.data.projectId,
-      modificationType: this.modificationType,
-      description: this.description,
-      expectedTime: this.expectedTime,
-      contactInfo: this.contactInfo,
-      submittedAt: new Date().toISOString()
-    };
-    
-    this.dialogRef.close({ confirmed: true, data: formData });
-  }
-}

+ 0 - 600
src/app/pages/customer-service/project-detail/project-detail.html

@@ -1,600 +0,0 @@
-<!-- 项目详情页面内容 -->
-<div class="project-detail-container ios-style">
-  <!-- 顶部导航/Header -->
-    <header class="project-header ios-header">
-      <div class="header-content">
-        <div class="project-info">
-          <h1 class="project-title">{{ project()?.name || '现代简约风格三居室设计' }}</h1>
-          <div class="project-meta">
-            <span class="project-status {{ getProjectStatusClass(project()?.status) }}">
-              {{ project()?.status || '进行中' }}
-            </span>
-            <span class="project-stage">当前阶段:{{ project()?.currentStage || '方案修改与确认' }}</span>
-            <span class="project-date">最后更新:{{ formatDate(currentDate()) }}</span>
-          </div>
-        </div>
-        <div class="header-actions">
-          <button class="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>
-              <line x1="16" y1="13" x2="8" y2="13"></line>
-              <line x1="16" y1="17" x2="8" y2="17"></line>
-              <polyline points="10 9 9 9 8 9"></polyline>
-            </svg>
-            <span>导出报告</span>
-          </button>
-          <button class="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>
-        </div>
-      </div>
-    </header>
-
-  <!-- 主要内容区域 -->
-  <div class="main-content-area ios-content">
-    <!-- 主内容区 (居中) -->
-    <div class="project-content-main ios-main">
-      <!-- 项目进度卡片 -->
-      <div class="card progress-card">
-        <div class="progress-header">
-          <h3>项目进度</h3>
-          <span class="progress-percentage">{{ completionProgress() }}%</span>
-        </div>
-        <div class="progress-bar">
-          <div class="progress-fill" [style.width]="progressFillWidth()"></div>
-        </div>
-        <div class="progress-meta">
-          <span>开始时间:{{ formatDate(project()?.createdAt || '2023-06-01') }}</span>
-          <span>预计完成:{{ formatDate(project()?.deadline || '2023-07-15') }}</span>
-        </div>
-      </div>
-      
-      <!-- 历史服务记录 -->
-      <div class="card historical-records-card">
-        <div class="records-header">
-          <h3>历史服务记录</h3>
-        </div>
-        
-        <!-- 过往咨询记录 -->
-        <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 }}
-              </div>
-            </div>
-          </div>
-        </div>
-        
-        <!-- 合作项目 -->
-        <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>
-            </div>
-          </div>
-        </div>
-        
-        <!-- 历史反馈/评价 -->
-        <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 }}
-              </div>
-            </div>
-          </div>
-        </div>
-      </div>
-
-      <!-- 进度时间轴 -->
-      <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>
-              </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>
-                </div>
-                <div class="stage-responsible">负责人:{{ stage.responsible || '未分配' }}</div>
-              </div>
-              <div class="stage-details" *ngIf="stage.details">
-                <p>{{ stage.details }}</p>
-              </div>
-            </div>
-          </div>
-        </div>
-      </div>
-
-      <!-- 项目详情标签页 -->
-      <div class="project-tabs ios-tabs">
-        <div class="tab-header">
-          <button class="tab-btn btn-hover-effect" [class.active]="activeTab() === 'overview'" (click)="switchTab('overview')">
-            <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor">
-              <path d="M5 12h14M12 5l7 7-7 7"></path>
-            </svg>
-            <span>概览</span>
-          </button>
-          <button class="tab-btn btn-hover-effect" [class.active]="activeTab() === 'milestones'" (click)="switchTab('milestones')">
-            <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor">
-              <polyline points="22 12 18 12 15 21 9 3 6 12 2 12"></polyline>
-            </svg>
-            <span>里程碑</span>
-          </button>
-          <button class="tab-btn btn-hover-effect" [class.active]="activeTab() === 'tasks'" (click)="switchTab('tasks')">
-            <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor">
-              <polyline points="22 12 18 12 15 21 9 3 6 12 2 12"></polyline>
-            </svg>
-            <span>任务</span>
-          </button>
-          <button class="tab-btn btn-hover-effect" [class.active]="activeTab() === 'messages'" (click)="switchTab('messages')">
-            <svg width="18" height="18" 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>
-            <span>消息</span>
-          </button>
-          <button class="tab-btn btn-hover-effect" [class.active]="activeTab() === 'files'" (click)="switchTab('files')">
-            <svg width="18" height="18" 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>
-              <line x1="16" y1="13" x2="8" y2="13"></line>
-              <line x1="16" y1="17" x2="8" y2="17"></line>
-              <polyline points="10 9 9 9 8 9"></polyline>
-            </svg>
-            <span>文件</span>
-          </button>
-        </div>
-
-        <!-- 消息标签内容 -->
-        <div *ngIf="activeTab() === 'messages'" 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>
-                  </div>
-                  <div class="message-text">{{ message.content }}</div>
-                </div>
-              </div>
-            </div>
-            <div class="message-input-area">
-              <textarea 
-                [value]="newMessage()"
-                (input)="onMessageInput($event)"
-                placeholder="输入消息内容..."
-                rows="3"
-                (keydown.enter.shift)="$event.preventDefault()"
-                (keydown.enter)="sendMessage()"
-              ></textarea>
-              <div class="message-actions">
-                <button class="secondary-btn btn-hover-effect">
-                  <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor">
-                    <path d="M14.5 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7.5L14.5 2z"></path>
-                    <polyline points="14 2 14 8 20 8"></polyline>
-                  </svg>
-                  <span>上传文件</span>
-                </button>
-                <button class="primary-btn btn-hover-effect" (click)="sendMessage()" [disabled]="!newMessage().trim()">
-                  <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor">
-                    <line x1="22" y1="2" x2="11" y2="13"></line>
-                    <polygon points="22 2 15 22 11 13 2 9 22 2"></polygon>
-                  </svg>
-                  <span>发送</span>
-                </button>
-              </div>
-            </div>
-          </div>
-        </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>
-                  </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>
-                  <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>
-                  <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>
-                    </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>
-                </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>
-                  </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)">
-              </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 *ngIf="activeTab() === 'messages'" 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>
-                  </div>
-                  <div class="message-text">{{ message.content }}</div>
-                </div>
-              </div>
-            </div>
-      </div>
-    </div>
-
-    <!-- 右侧边栏 - 企业微信聊天集成 -->
-    <div class="wechat-sidebar ios-sidebar">
-      <div class="wechat-header">
-        <h3>项目群聊</h3>
-        <div class="wechat-actions">
-          <button class="search-btn">
-            <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
-              <circle cx="11" cy="11" r="8"></circle>
-              <path d="m21 21-4.35-4.35"></path>
-            </svg>
-          </button>
-          <button class="settings-btn">
-            <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
-              <circle cx="12" cy="12" r="3"></circle>
-              <path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"></path>
-            </svg>
-          </button>
-        </div>
-      </div>
-      
-      <!-- 聊天消息列表 -->
-      <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>
-            </div>
-            <div class="message-text">{{ msg.content }}</div>
-          </div>
-        </div>
-      </div>
-      
-      <!-- 消息输入框 -->
-      <div class="wechat-input-area">
-        <div class="input-actions">
-          <button class="action-btn btn-hover-effect">
-            <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor">
-              <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>
-          </button>
-          <button class="action-btn btn-hover-effect">
-            <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor">
-              <path d="M14.5 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7.5L14.5 2z"></path>
-              <polyline points="14 2 14 8 20 8"></polyline>
-            </svg>
-          </button>
-        </div>
-        <input 
-          type="text" 
-          [(ngModel)]="wechatInput" 
-          placeholder="输入消息..." 
-          class="wechat-input"
-          (keydown.enter)="sendWechatMessage()"
-        />
-        <button class="send-btn btn-hover-effect" (click)="sendWechatMessage()" [disabled]="!wechatInput.trim()">
-          发送
-        </button>
-      </div>
-    </div>
-  </div>
-
-  <!-- 售后处理入口 (固定在底部) -->
-  <div class="after-sales-actions ios-actions">
-    <div class="actions-container">
-      <button class="action-btn primary btn-hover-effect" (click)="openModificationRequest()">
-        <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor">
-          <path d="M12 20h9"></path>
-          <path d="M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5z"></path>
-        </svg>
-        <span>申请修改</span>
-      </button>
-      <button class="action-btn warning btn-hover-effect" (click)="openComplaintWarning()">
-        <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor">
-          <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>
-        <span>投诉预警</span>
-      </button>
-      <button class="action-btn secondary btn-hover-effect" (click)="openRefundRequest()">
-        <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor">
-          <path d="M22 12h-4l-3 9L9 3l-3 9H2"></path>
-        </svg>
-        <span>申请退款</span>
-      </button>
-    </div>
-  </div>
-</div>
-
-<!-- 文件标签内容 -->
-@if (activeTab() === 'files') {
-  <div class="tab-content">
-    <div class="files-header">
-      <h4>项目文件</h4>
-      <div class="files-actions">
-        <button class="primary-btn btn-hover-effect" (click)="openRenderPreview()" [disabled]="renderImages().length === 0">
-          <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
-            <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>
-          <span>查看渲染图</span>
-        </button>
-      </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>
-        </div>
-      </div>
-    </div>
-  </div>
-}
-
-<!-- 只读渲染图预览弹窗 -->
-@if (showRenderPreviewModal) {
-  <div class="modal-backdrop" (click)="closeRenderPreview()"></div>
-  <div class="modal" role="dialog" aria-modal="true">
-    <div class="modal-header">
-      <h3>渲染图预览</h3>
-      <button class="close-button" (click)="closeRenderPreview()" aria-label="关闭">×</button>
-    </div>
-    <div class="modal-body">
-      @if (renderImages().length > 0) {
-        <div class="thumb-list">
-          @for (img of renderImages(); track img.id) {
-            <div class="thumb-item">
-              <img [src]="img.url" [alt]="img.name" />
-              <div class="thumb-meta">
-                <div class="name">{{ img.name }}</div>
-                <div class="sub">{{ img.size }} · {{ formatDate(img.uploadedAt) }}</div>
-              </div>
-            </div>
-          }
-        </div>
-      } @else {
-        <div class="empty">
-          <p>暂无可预览的渲染图</p>
-        </div>
-      }
-    </div>
-    <div class="modal-footer">
-      <button class="secondary-btn" (click)="closeRenderPreview()">关闭</button>
-    </div>
-  </div>
-}

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

@@ -1,2706 +0,0 @@
-@use "sass:color";
-
-// iOS风格全局变量
-$primary-color: #007AFF;
-$primary-dark: #0047AB;
-$success-color: #34C759;
-$warning-color: #FF9500;
-$danger-color: #FF3B30;
-$text-primary: #1C1C1E;
-$text-secondary: #636366;
-$text-tertiary: #8E8E93;
-$border-color: #D1D1D6;
-$background-primary: #F2F2F7;
-$background-secondary: #FFFFFF;
-$background-tertiary: #E5E5EA;
-$shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.1);
-$shadow-md: 0 4px 6px rgba(0, 0, 0, 0.1);
-$shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.1);
-$border-radius: 12px;
-$transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
-
-// 兼容背景变量(别名)
-$bg-light: $background-secondary;
-$bg-white: $background-primary;
-
-// iOS风格卡片
-.card {
-  background: $background-secondary;
-  border-radius: $border-radius;
-  padding: 16px;
-  margin-bottom: 16px;
-  box-shadow: $shadow-sm;
-  transition: $transition;
-  
-  &:hover {
-    box-shadow: $shadow-md;
-  }
-}
-
-// Members & Requirements — iOS styled details and micro-interactions
-.project-detail-container {
-  // Members tab
-  .members-header {
-    display: flex;
-    align-items: center;
-    justify-content: space-between;
-    margin: 12px 0 8px;
-
-    h4 {
-      margin: 0;
-      font-size: 16px;
-      font-weight: 600;
-      color: $text-primary;
-    }
-
-    .actions {
-      display: flex;
-      gap: 8px;
-
-      .primary-btn,
-      .secondary-btn {
-        height: 34px;
-        padding: 0 14px;
-        border-radius: 10px;
-      }
-    }
-  }
-
-  .members-list {
-    background-color: $bg-white;
-    border: 1px solid $border-color;
-    border-radius: 12px;
-    box-shadow: $shadow-sm;
-    padding: 8px;
-  }
-
-  .member-row {
-    display: flex;
-    align-items: center;
-    gap: 12px;
-    padding: 12px 10px;
-    border-bottom: 1px dashed color-mix(in srgb, $border-color 70%, transparent);
-    transition: $transition;
-
-    &:last-child { border-bottom: none; }
-
-    .role {
-      flex: 0 0 120px;
-      font-size: 13px;
-      color: $text-secondary;
-    }
-
-    .name { flex: 1; }
-
-    &.editable:hover { background-color: $bg-light; }
-  }
-
-  .member-input {
-    width: 100%;
-    background: $bg-light;
-    border: 1px solid $border-color;
-    border-radius: 10px;
-    padding: 8px 12px;
-    font-size: 14px;
-    color: $text-primary;
-    outline: none;
-    transition: $transition;
-
-    &::placeholder { color: $text-tertiary; }
-
-    &:focus {
-      background: $bg-white;
-      border-color: $primary-color;
-      box-shadow: 0 0 0 3px color-mix(in srgb, $primary-color 15%, transparent);
-      transform: translateY(-1px);
-    }
-  }
-
-  // Requirements tab
-  .requirements-header {
-    margin: 12px 0 8px;
-
-    h4 {
-      margin: 0;
-      font-size: 16px;
-      font-weight: 600;
-      color: $text-primary;
-    }
-  }
-
-  .requirements-form {
-    background: $bg-white;
-    border: 1px solid $border-color;
-    border-radius: 12px;
-    box-shadow: $shadow-sm;
-    padding: 12px;
-    display: flex;
-    flex-direction: column;
-    gap: 12px;
-
-    .form-row {
-      display: flex;
-      flex-direction: column;
-      gap: 6px;
-
-      label {
-        font-size: 13px;
-        color: $text-secondary;
-      }
-
-      .form-input,
-      .form-textarea {
-        background: $bg-light;
-        border: 1px solid $border-color;
-        border-radius: 12px;
-        padding: 10px 12px;
-        font-size: 14px;
-        color: $text-primary;
-        outline: none;
-        transition: $transition;
-
-        &::placeholder { color: $text-tertiary; }
-
-        &:focus {
-          background: $bg-white;
-          border-color: $primary-color;
-          box-shadow: 0 0 0 3px color-mix(in srgb, $primary-color 15%, transparent);
-          transform: translateY(-1px);
-        }
-      }
-
-      .form-textarea { resize: vertical; min-height: 84px; }
-    }
-
-    .form-actions {
-      display: flex;
-      justify-content: flex-end;
-      gap: 10px;
-      padding-top: 8px;
-      border-top: 1px solid color-mix(in srgb, $border-color 70%, transparent);
-    }
-  }
-}
-
-// Responsive fine-tuning
-@media (max-width: 768px) {
-  .project-detail-container {
-    .members-list { padding: 6px; }
-    .member-row {
-      padding: 10px 8px;
-      .role { flex-basis: 88px; }
-    }
-
-    .requirements-form { padding: 10px; }
-  }
-}
-
-// iOS风格按钮
-.button {
-  border-radius: $border-radius;
-  padding: 12px 20px;
-  font-weight: 500;
-  transition: $transition;
-  display: inline-flex;
-  align-items: center;
-  justify-content: center;
-  
-  &-primary {
-    background: $primary-color;
-    color: white;
-    
-    &:active {
-      background: #0E42CB;
-    }
-  }
-  
-  &-secondary {
-    background: $background-tertiary;
-    color: $text-primary;
-    
-    &:active {
-      background: #D1D1D6;
-    }
-  }
-}
-
-// 按钮点击反馈效果
-.btn-hover-effect {
-  transition: $transition;
-  transform: scale(1);
-  position: relative;
-  overflow: hidden;
-}
-
-.btn-hover-effect:active {
-  transform: scale(0.95);
-  transition: all 0.1s ease;
-}
-
-.btn-hover-effect::after {
-  content: '';
-  position: absolute;
-  top: 50%;
-  left: 50%;
-  width: 0;
-  height: 0;
-  border-radius: 50%;
-  background-color: rgba(255, 255, 255, 0.3);
-  transform: translate(-50%, -50%);
-  transition: width 0.6s, height 0.6s;
-}
-
-.btn-hover-effect:active::after {
-  width: 300px;
-  height: 300px;
-}
-
-// 主要按钮的特定点击效果
-.primary-btn.btn-hover-effect:active {
-  background-color: #0E42CB;
-}
-
-// 次要按钮的特定点击效果
-.secondary-btn.btn-hover-effect:active {
-  background-color: #E5E6EB;
-}
-
-// 操作按钮的特定点击效果
-.action-btn.btn-hover-effect:active {
-  transform: scale(0.9);
-  background-color: color-mix(in srgb, $primary-color 5%, transparent);
-}
-
-// 主容器
-.project-detail-container {
-  display: flex;
-  flex-direction: column;
-  height: 100vh;
-  overflow: hidden;
-}
-
-// 顶部导航/Header
-.project-header-blue {
-  background-color: $primary-dark;
-  color: white;
-  padding: 16px 24px;
-  box-shadow: $shadow-md;
-  z-index: 100;
-
-  .header-content {
-    display: flex;
-    justify-content: space-between;
-    align-items: center;
-    max-width: 1400px;
-    margin: 0 auto;
-  }
-
-  .project-info {
-    flex: 1;
-  }
-
-  .project-title {
-    font-size: 24px;
-    font-weight: 600;
-    margin-bottom: 8px;
-  }
-
-  .project-meta {
-    display: flex;
-    gap: 16px;
-    font-size: 14px;
-  }
-
-  .project-status {
-    padding: 4px 8px;
-    border-radius: 4px;
-    font-weight: 500;
-    background-color: rgba(255, 255, 255, 0.2);
-  }
-
-  .project-stage,
-  .project-date {
-    opacity: 0.9;
-  }
-
-  .header-actions {
-    display: flex;
-    gap: 12px;
-  }
-
-  .primary-btn,
-  .secondary-btn {
-    color: white;
-    background-color: rgba(255, 255, 255, 0.2);
-    border: 1px solid rgba(255, 255, 255, 0.3);
-    padding: 8px 16px;
-    border-radius: $border-radius;
-    cursor: pointer;
-    display: flex;
-    align-items: center;
-    gap: 6px;
-    transition: $transition;
-
-    &:hover {
-      background-color: rgba(255, 255, 255, 0.3);
-    }
-  }
-
-  .primary-btn {
-    background-color: $primary-color;
-    border-color: $primary-color;
-
-    &:hover {
-      background-color: #0E42CB;
-    }
-  }
-}
-
-// 主要内容区域
-.main-content-area {
-  display: flex;
-  flex: 1;
-  overflow: hidden;
-}
-
-// 主内容区 (居中)
-.project-content-main {
-  flex: 1;
-  overflow-y: auto;
-  padding: 24px;
-  background-color: $background-secondary;
-}
-
-// 右侧边栏 - 企业微信聊天集成
-.wechat-sidebar {
-  width: 350px;
-  background-color: $background-tertiary;
-  border-left: 1px solid $border-color;
-  display: flex;
-  flex-direction: column;
-  overflow: hidden;
-}
-
-.wechat-header {
-  padding: 16px 20px;
-  border-bottom: 1px solid $border-color;
-  background-color: $background-primary;
-  display: flex;
-  justify-content: space-between;
-  align-items: center;
-}
-
-.wechat-header h3 {
-  font-size: 16px;
-  font-weight: 600;
-  color: $text-primary;
-}
-
-.wechat-actions {
-  display: flex;
-  gap: 8px;
-}
-
-.search-btn,
-.settings-btn {
-  background: none;
-  border: none;
-  padding: 6px;
-  border-radius: 4px;
-  cursor: pointer;
-  color: $text-secondary;
-  transition: $transition;
-
-  &:hover {
-    background-color: $background-tertiary;
-    color: $primary-color;
-  }
-}
-
-.wechat-messages {
-  flex: 1;
-  overflow-y: auto;
-  padding: 16px;
-}
-
-.wechat-message-item {
-  display: flex;
-  gap: 12px;
-  margin-bottom: 16px;
-}
-
-.message-avatar {
-  width: 36px;
-  height: 36px;
-  border-radius: 50%;
-  background-color: $primary-color;
-  color: white;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  font-weight: 600;
-  font-size: 14px;
-  flex-shrink: 0;
-}
-
-.message-content {
-  flex: 1;
-}
-
-.message-header {
-  display: flex;
-  justify-content: space-between;
-  margin-bottom: 4px;
-  font-size: 12px;
-}
-
-.message-sender {
-  font-weight: 600;
-  color: $text-primary;
-}
-
-.message-time {
-  color: $text-tertiary;
-}
-
-.message-text {
-  background-color: $background-primary;
-  padding: 8px 12px;
-  border-radius: $border-radius;
-  color: $text-primary;
-  line-height: 1.5;
-}
-
-.wechat-input-area {
-  padding: 16px;
-  border-top: 1px solid $border-color;
-  background-color: $background-primary;
-  display: flex;
-  gap: 8px;
-  align-items: flex-end;
-}
-
-.input-actions {
-  display: flex;
-  flex-direction: column;
-  gap: 8px;
-}
-
-.action-btn {
-  background: none;
-  border: none;
-  padding: 8px;
-  border-radius: 4px;
-  cursor: pointer;
-  color: $text-secondary;
-  transition: $transition;
-
-  &:hover {
-    background-color: $background-tertiary;
-    color: $primary-color;
-  }
-}
-
-.wechat-input {
-  flex: 1;
-  padding: 10px 12px;
-  border: 1px solid $border-color;
-  border-radius: $border-radius;
-  outline: none;
-  font-size: 14px;
-  transition: $transition;
-
-  &:focus {
-    border-color: $primary-color;
-  }
-}
-
-.send-btn {
-  padding: 10px 16px;
-  background-color: $primary-color;
-  color: white;
-  border: none;
-  border-radius: $border-radius;
-  cursor: pointer;
-  font-size: 14px;
-  transition: $transition;
-
-  &:hover:not(:disabled) {
-    background-color: $primary-dark;
-  }
-
-  &:disabled {
-    background-color: $text-tertiary;
-    cursor: not-allowed;
-  }
-}
-
-// 售后处理入口 (固定在底部)
-.after-sales-actions {
-  position: fixed;
-  bottom: 0;
-  left: 0;
-  right: 350px; /* 与右侧边栏宽度一致 */
-  background-color: $background-primary;
-  border-top: 1px solid $border-color;
-  padding: 16px 24px;
-  box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.05);
-  z-index: 100;
-}
-
-.actions-container {
-  max-width: 1000px;
-  margin: 0 auto;
-  display: flex;
-  justify-content: center;
-  gap: 16px;
-}
-
-.action-btn {
-  display: flex;
-  align-items: center;
-  gap: 8px;
-  padding: 10px 20px;
-  border-radius: $border-radius;
-  border: none;
-  cursor: pointer;
-  font-size: 14px;
-  font-weight: 500;
-  transition: $transition;
-}
-
-.action-btn.primary {
-  background-color: $primary-dark;
-  color: white;
-
-  &:hover {
-    background-color: #0a2340;
-  }
-}
-
-.action-btn.warning {
-  background-color: $warning-color;
-  color: white;
-
-  &:hover {
-    background-color: #e57000;
-  }
-}
-
-.action-btn.secondary {
-  background-color: $background-tertiary;
-  color: $text-primary;
-  border: 1px solid $border-color;
-
-  &:hover {
-    background-color: $background-secondary;
-  }
-}
-
-// 进度卡片
-.progress-card {
-  background-color: $background-primary;
-  border-radius: $border-radius;
-  padding: 20px;
-  margin-bottom: 24px;
-  box-shadow: $shadow-sm;
-}
-
-.progress-header {
-  display: flex;
-  justify-content: space-between;
-  align-items: center;
-  margin-bottom: 12px;
-}
-
-.progress-header h3 {
-  font-size: 16px;
-  font-weight: 600;
-  color: $text-primary;
-}
-
-.progress-percentage {
-  font-size: 24px;
-  font-weight: 600;
-  color: $primary-color;
-}
-
-.progress-bar {
-  height: 8px;
-  background-color: $background-tertiary;
-  border-radius: 4px;
-  overflow: hidden;
-  margin-bottom: 12px;
-}
-
-.progress-fill {
-  height: 100%;
-  background-color: $primary-color;
-  transition: width 0.3s ease;
-}
-
-.progress-meta {
-  display: flex;
-  justify-content: space-between;
-  font-size: 12px;
-  color: $text-tertiary;
-}
-
-// 进度时间轴卡片
-.timeline-card {
-  background-color: $background-primary;
-  border-radius: $border-radius;
-  padding: 20px;
-  margin-bottom: 24px;
-  box-shadow: $shadow-sm;
-}
-
-.card-title {
-  font-size: 16px;
-  font-weight: 600;
-  color: $text-primary;
-  margin-bottom: 20px;
-}
-
-.project-timeline {
-  position: relative;
-  padding-left: 30px;
-}
-
-.timeline-item {
-  position: relative;
-  padding-bottom: 24px;
-  transition: $transition;
-
-  &:hover {
-    background-color: $background-tertiary;
-    border-radius: $border-radius;
-  }
-}
-
-.timeline-icon {
-  position: absolute;
-  left: -30px;
-  top: 4px;
-  width: 24px;
-  height: 24px;
-  border-radius: 50%;
-  background-color: $background-tertiary;
-  border: 2px solid $border-color;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  transition: $transition;
-}
-
-.timeline-icon.icon-completed {
-  background-color: $success-color;
-  border-color: $success-color;
-  color: white;
-}
-
-.timeline-icon.icon-in-progress {
-  background-color: $primary-color;
-  border-color: $primary-color;
-  color: white;
-  animation: pulse 2s infinite;
-}
-
-.timeline-line {
-  position: absolute;
-  left: -24px;
-  top: 28px;
-  width: 2px;
-  height: calc(100% + 4px);
-  background-color: $border-color;
-}
-
-.timeline-line.line-completed {
-  background-color: $success-color;
-}
-
-.timeline-content {
-  padding: 8px 16px;
-}
-
-.timeline-header {
-  display: flex;
-  justify-content: space-between;
-  align-items: center;
-  margin-bottom: 8px;
-}
-
-.stage-title {
-  font-size: 14px;
-  font-weight: 600;
-  color: $text-primary;
-}
-
-.stage-status {
-  font-size: 12px;
-  padding: 2px 8px;
-  border-radius: 12px;
-  background-color: $background-tertiary;
-  color: $text-secondary;
-}
-
-.stage-item.stage-completed .stage-status {
-  background-color: color-mix(in srgb, $success-color 10%, transparent);
-  color: $success-color;
-}
-
-.stage-item.stage-in-progress .stage-status {
-  background-color: color-mix(in srgb, $primary-color 10%, transparent);
-  color: $primary-color;
-}
-
-.timeline-meta {
-  font-size: 12px;
-  color: $text-tertiary;
-  margin-bottom: 8px;
-}
-
-.stage-dates {
-  display: flex;
-  gap: 16px;
-  margin-bottom: 4px;
-}
-
-.stage-details {
-  font-size: 13px;
-  color: $text-secondary;
-  line-height: 1.4;
-}
-
-// 标签页样式
-.project-tabs {
-  background-color: $background-primary;
-  border-radius: $border-radius;
-  box-shadow: $shadow-sm;
-  overflow: hidden;
-}
-
-.tab-header {
-  display: flex;
-  border-bottom: 1px solid $border-color;
-  background-color: $background-tertiary;
-}
-
-.tab-btn {
-  display: flex;
-  align-items: center;
-  gap: 8px;
-  padding: 14px 20px;
-  border: none;
-  background: none;
-  cursor: pointer;
-  font-size: 14px;
-  color: $text-secondary;
-  transition: $transition;
-
-  &:hover {
-    color: $primary-color;
-    background-color: color-mix(in srgb, $primary-color 5%, transparent);
-  }
-
-  &.active {
-    color: $primary-color;
-    background-color: $background-primary;
-    border-bottom: 2px solid $primary-color;
-  }
-}
-
-.tab-content {
-  padding: 20px;
-}
-
-// iOS风格动画效果
-@keyframes pulse {
-  0% {
-    transform: scale(1);
-    opacity: 1;
-  }
-  50% {
-    transform: scale(0.95);
-    opacity: 0.8;
-  }
-  100% {
-    transform: scale(1);
-    opacity: 1;
-  }
-}
-
-@keyframes fadeIn {
-  from {
-    opacity: 0;
-    transform: translateY(10px);
-  }
-  to {
-    opacity: 1;
-    transform: translateY(0);
-  }
-}
-
-@keyframes sending {
-  0% {
-    transform: scale(1);
-  }
-  50% {
-    transform: scale(0.9);
-  }
-  100% {
-    transform: scale(1);
-  }
-}
-
-@keyframes completing {
-  0% {
-    transform: translateY(0);
-    opacity: 1;
-  }
-  100% {
-    transform: translateY(-20px);
-    opacity: 0;
-  }
-}
-
-// 按钮发送动画
-.sending {
-  animation: sending 0.3s ease;
-}
-
-// 任务完成动画
-.completing {
-  animation: completing 0.5s ease forwards;
-}
-
-// 消息进入动画
-.message-item {
-  animation: fadeIn 0.3s ease;
-}
-
-// iOS风格弹性动画
-.ios-bounce {
-  transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
-}
-
-// 标签切换动画
-.tab-content {
-  animation: fadeIn 0.3s ease;
-}
-
-// 响应式设计
-@media (max-width: 1200px) {
-  .wechat-sidebar {
-    width: 300px;
-  }
-  
-  .after-sales-actions {
-    right: 300px;
-  }
-}
-
-@media (max-width: 768px) {
-  .main-content-area {
-    flex-direction: column;
-  }
-  
-  .wechat-sidebar {
-    width: 100%;
-    height: 0;
-    border-left: none;
-    border-top: 1px solid $border-color;
-  }
-  
-  .wechat-sidebar.show {
-    height: 400px;
-  }
-  
-  .after-sales-actions {
-    right: 0;
-    bottom: 0;
-  }
-  
-  .actions-container {
-    flex-wrap: wrap;
-  }
-}
-
-// 原有样式变量兼容
-$bg-light: $background-secondary;
-$bg-white: $background-primary;
-$box-shadow: $shadow-md;
-$text-light: $text-tertiary;
-
-// 顶部导航栏
-.top-navbar {
-  display: flex;
-  justify-content: space-between;
-  align-items: center;
-  padding: 0 24px;
-  height: 64px;
-  background-color: $bg-white;
-  border-bottom: 1px solid $border-color;
-  box-shadow: $box-shadow;
-
-  .navbar-left {
-    display: flex;
-    align-items: center;
-    gap: 16px;
-
-    .menu-toggle {
-      display: flex;
-      align-items: center;
-      justify-content: center;
-      width: 40px;
-      height: 40px;
-      border: none;
-      background: none;
-      color: $text-primary;
-      cursor: pointer;
-      transition: $transition;
-
-      &:hover {
-        color: $primary-color;
-        background-color: $bg-light;
-        border-radius: 4px;
-      }
-    }
-
-    .app-title {
-      font-size: 18px;
-      font-weight: 600;
-      color: $text-primary;
-      margin: 0;
-    }
-  }
-
-  .navbar-right {
-    display: flex;
-    align-items: center;
-    gap: 16px;
-
-    .notification-btn {
-      display: flex;
-      align-items: center;
-      justify-content: center;
-      width: 40px;
-      height: 40px;
-      border: none;
-      background: none;
-      color: $text-primary;
-      cursor: pointer;
-      position: relative;
-      transition: $transition;
-
-      &:hover {
-        color: $primary-color;
-        background-color: $bg-light;
-        border-radius: 4px;
-      }
-
-      .notification-badge {
-        position: absolute;
-        top: 8px;
-        right: 8px;
-        display: flex;
-        align-items: center;
-        justify-content: center;
-        width: 16px;
-        height: 16px;
-        font-size: 10px;
-        font-weight: 600;
-        color: white;
-        background-color: $danger-color;
-        border-radius: 50%;
-      }
-    }
-
-    .user-profile {
-      display: flex;
-      align-items: center;
-      gap: 8px;
-      padding: 8px 12px;
-      border-radius: 6px;
-      transition: $transition;
-
-      &:hover {
-        background-color: $bg-light;
-      }
-
-      .user-avatar {
-        width: 32px;
-        height: 32px;
-        border-radius: 50%;
-        object-fit: cover;
-      }
-
-      .user-name {
-        font-size: 14px;
-        font-weight: 500;
-        color: $text-primary;
-      }
-    }
-  }
-}
-
-// 主要内容区
-.dashboard-content {
-  display: flex;
-  flex: 1;
-  overflow: hidden;
-}
-
-// 左侧侧边栏
-.sidebar {
-  width: 240px;
-  background-color: $bg-white;
-  border-right: 1px solid $border-color;
-  overflow-y: auto;
-  flex-shrink: 0;
-
-  .sidebar-nav {
-    padding: 16px 0;
-
-    .nav-item {
-      display: flex;
-      align-items: center;
-      gap: 12px;
-      padding: 12px 24px;
-      color: $text-secondary;
-      text-decoration: none;
-      transition: $transition;
-
-      &:hover {
-        color: $primary-color;
-        background-color: $bg-light;
-      }
-
-      &.active {
-        color: $primary-color;
-        background-color: #e3f2fd;
-        border-left: 3px solid $primary-color;
-        padding-left: 21px;
-      }
-
-      svg {
-        flex-shrink: 0;
-      }
-
-      span {
-        font-size: 14px;
-        font-weight: 500;
-      }
-    }
-  }
-}
-
-// 右侧主要内容
-.project-content {
-  flex: 1;
-  overflow-y: auto;
-  padding: 24px;
-
-  // 项目头部信息
-  .project-header {
-    display: flex;
-    justify-content: space-between;
-    align-items: flex-start;
-    margin-bottom: 24px;
-
-    .project-title-section {
-      .project-title {
-        font-size: 24px;
-        font-weight: 600;
-        color: $text-primary;
-        margin: 0 0 8px 0;
-      }
-
-      .project-meta {
-        display: flex;
-        align-items: center;
-        gap: 16px;
-
-        .project-status {
-          padding: 4px 12px;
-          border-radius: 16px;
-          font-size: 12px;
-          font-weight: 500;
-          text-align: center;
-
-          &.status-active {
-            color: $primary-color;
-            background-color: #e3f2fd;
-          }
-
-          &.status-pending {
-            color: $warning-color;
-            background-color: #fff3e0;
-          }
-
-          &.status-completed {
-            color: $success-color;
-            background-color: #e8f5e9;
-          }
-        }
-
-        .project-stage {
-          font-size: 14px;
-          color: $text-secondary;
-        }
-      }
-    }
-
-    .project-actions {
-      display: flex;
-      gap: 12px;
-
-      .primary-btn,
-      .secondary-btn {
-        display: flex;
-        align-items: center;
-        gap: 8px;
-        padding: 8px 16px;
-        border: none;
-        border-radius: 6px;
-        font-size: 14px;
-        font-weight: 500;
-        cursor: pointer;
-        transition: $transition;
-      }
-
-      .primary-btn {
-        color: white;
-        background-color: $primary-color;
-
-        &:hover {
-          background-color: #1565c0;
-        }
-
-        &:disabled {
-          background-color: $text-tertiary;
-          cursor: not-allowed;
-        }
-      }
-
-      .secondary-btn {
-        color: $text-primary;
-        background-color: $bg-white;
-        border: 1px solid $border-color;
-
-        &:hover {
-          background-color: $bg-light;
-        }
-      }
-
-      .primary-btn.small {
-        padding: 4px 12px;
-        font-size: 12px;
-      }
-    }
-  }
-
-  // 项目进度卡片
-  .progress-card {
-    padding: 20px;
-    margin-bottom: 24px;
-    background-color: $bg-white;
-    border-radius: 8px;
-    box-shadow: $box-shadow;
-
-    .progress-header {
-      display: flex;
-      justify-content: space-between;
-      align-items: center;
-      margin-bottom: 12px;
-
-      h3 {
-        font-size: 16px;
-        font-weight: 600;
-        color: $text-primary;
-        margin: 0;
-      }
-
-      .progress-percentage {
-        font-size: 20px;
-        font-weight: 700;
-        color: $primary-color;
-      }
-    }
-
-    .progress-bar {
-      height: 8px;
-      margin-bottom: 12px;
-      background-color: $bg-light;
-      border-radius: 4px;
-      overflow: hidden;
-
-      .progress-fill {
-        height: 100%;
-        background-color: $primary-color;
-        border-radius: 4px;
-        transition: $transition;
-      }
-    }
-
-    .progress-meta {
-      display: flex;
-      justify-content: space-between;
-      font-size: 12px;
-      color: $text-tertiary;
-    }
-  }
-
-  // 项目详情标签页
-  .project-tabs {
-    background-color: $bg-white;
-    border-radius: 8px;
-    box-shadow: $box-shadow;
-    overflow: hidden;
-
-    // 标签页头部
-    .tab-header {
-      display: flex;
-      border-bottom: 1px solid $border-color;
-      background-color: #f8f9fa;
-
-      .tab-btn {
-        display: flex;
-        align-items: center;
-        gap: 8px;
-        padding: 16px 24px;
-        border: none;
-        background: none;
-        font-size: 14px;
-        font-weight: 500;
-        color: $text-secondary;
-        cursor: pointer;
-        transition: $transition;
-        border-bottom: 2px solid transparent;
-
-        &:hover {
-          color: $primary-color;
-          background-color: $bg-light;
-        }
-
-        &.active {
-          color: $primary-color;
-          background-color: $bg-white;
-          border-bottom-color: $primary-color;
-        }
-      }
-    }
-
-    // 标签页内容
-    .tab-content {
-      padding: 24px;
-      min-height: 400px;
-    }
-  }
-
-  // 概览网格布局
-  .overview-grid {
-    display: grid;
-    grid-template-columns: repeat(auto-fill, minmax(350px, 1fr));
-    gap: 24px;
-
-    // 信息卡片
-    .info-card {
-      padding: 20px;
-      background-color: color-mix(in srgb, $bg-light 80%, $bg-white);
-      border-radius: 10px;
-
-      .card-title {
-        font-size: 16px;
-        font-weight: 700;
-        color: $text-primary;
-        margin: 0 0 14px 0;
-        padding-bottom: 10px;
-        border-bottom: 1px solid color-mix(in srgb, $border-color 80%, transparent);
-        letter-spacing: .2px;
-      }
-
-      // 客户信息
-      .customer-info {
-        .info-item {
-          margin-bottom: 16px;
-
-          label {
-            display: block;
-            font-size: 12px;
-            font-weight: 600;
-            color: $text-secondary;
-            letter-spacing: .2px;
-            margin-bottom: 4px;
-          }
-
-          span {
-            font-size: 14px;
-            font-weight: 500;
-            color: $text-primary;
-            font-variant-numeric: tabular-nums;
-            letter-spacing: .1px;
-          }
-
-          .tag-container {
-            display: flex;
-            gap: 8px;
-            flex-wrap: wrap;
-
-            .tag {
-              padding: 4px 10px;
-              font-size: 12px;
-              font-weight: 600;
-              color: color.mix($primary-color, black, 85%);
-              background-color: color-mix(in srgb, #e3f2fd 80%, white);
-              border-radius: 14px;
-              letter-spacing: .2px;
-            }
-          }
-
-          .need-list {
-            margin: 0;
-            padding-left: 20px;
-
-            li {
-              display: flex;
-              align-items: flex-start;
-              gap: 8px;
-              font-size: 14px;
-              color: $text-primary;
-              margin-bottom: 4px;
-
-              svg {
-                flex-shrink: 0;
-                margin-top: 2px;
-                color: $success-color;
-              }
-            }
-          }
-        }
-      }
-
-      // 团队信息
-      .team-info {
-        .team-member {
-          display: flex;
-          align-items: center;
-          justify-content: space-between;
-          padding: 12px 0;
-          border-bottom: 1px solid $border-color;
-
-          &:last-child {
-            border-bottom: none;
-          }
-
-          .member-avatar {
-            width: 48px;
-            height: 48px;
-            border-radius: 50%;
-            object-fit: cover;
-            display: flex;
-            align-items: center;
-            justify-content: center;
-            background: linear-gradient(180deg, #f5f7fa, #e9edf3);
-            color: $text-secondary;
-            font-weight: 600;
-            font-size: 14px;
-            border: 1px solid $border-color;
-            box-shadow: 0 1px 2px rgba(0,0,0,0.04);
-          }
-
-          .member-details {
-            flex: 1;
-            margin-left: 12px;
-
-            .member-name {
-              font-size: 15px;
-              font-weight: 600;
-              color: $text-primary;
-              margin-bottom: 2px;
-              letter-spacing: .2px;
-            }
-
-            .member-role {
-              font-size: 12px;
-              color: $text-secondary;
-            }
-          }
-
-          .message-btn {
-            display: flex;
-            align-items: center;
-            justify-content: center;
-            width: 36px;
-            height: 36px;
-            border: none;
-            color: $primary-color;
-            background-color: #e3f2fd;
-            border-radius: 50%;
-            cursor: pointer;
-            transition: $transition;
-
-            &:hover {
-              background-color: $primary-color;
-              color: white;
-              box-shadow: 0 8px 16px rgba(0,0,0,0.12);
-              transform: translateY(-1px);
-            }
-
-            &:active {
-              transform: translateY(0);
-              box-shadow: 0 4px 10px rgba(0,0,0,0.08);
-            }
-          }
-        }
-      }
-
-      // 反馈信息
-      .feedback-list {
-        .feedback-item {
-          padding: 16px;
-          background-color: $bg-white;
-          border-radius: 6px;
-          margin-bottom: 12px;
-
-          .feedback-header {
-            display: flex;
-            justify-content: space-between;
-            align-items: center;
-            margin-bottom: 8px;
-
-            .feedback-author {
-              font-size: 14px;
-              font-weight: 500;
-              color: $text-primary;
-            }
-
-            .feedback-rating {
-              display: flex;
-              align-items: center;
-              gap: 8px;
-
-              .fa {
-                color: $warning-color;
-                margin-right: 2px;
-              }
-
-              .rating-stars {
-                font-size: 12px;
-                color: $warning-color;
-              }
-
-              .rating-number {
-                font-size: 14px;
-                font-weight: 600;
-                color: $text-primary;
-              }
-            }
-          }
-
-          .feedback-content {
-            font-size: 14px;
-            color: $text-primary;
-            line-height: 1.6;
-            margin-bottom: 8px;
-          }
-
-          .feedback-response {
-            padding: 12px;
-            background-color: $bg-light;
-            border-radius: 4px;
-            margin-bottom: 8px;
-
-            .response-label {
-              font-size: 12px;
-              font-weight: 500;
-              color: $text-light;
-              margin-bottom: 4px;
-            }
-
-            .response-text {
-              font-size: 13px;
-              color: $text-secondary;
-            }
-          }
-
-          .feedback-meta {
-            display: flex;
-            justify-content: space-between;
-            align-items: center;
-
-            .feedback-date {
-              font-size: 12px;
-              color: $text-light;
-            }
-
-            .feedback-status {
-              padding: 2px 8px;
-              font-size: 11px;
-              font-weight: 500;
-              border-radius: 12px;
-
-              &.status-processed {
-                color: $success-color;
-                background-color: #e8f5e9;
-              }
-
-              &.status-pending {
-                color: $warning-color;
-                background-color: #fff3e0;
-              }
-            }
-          }
-        }
-
-        .view-all-btn {
-          display: block;
-          width: 100%;
-          padding: 8px 16px;
-          border: 1px solid $border-color;
-          background-color: $bg-white;
-          color: $text-secondary;
-          font-size: 13px;
-          text-align: center;
-          cursor: pointer;
-          border-radius: 4px;
-          transition: $transition;
-
-          &:hover {
-            color: $primary-color;
-            border-color: $primary-color;
-          }
-        }
-      }
-    }
-  }
-
-  // 里程碑时间线
-  .milestones-timeline {
-    position: relative;
-    padding-left: 32px;
-    margin-left: 16px;
-
-    .milestone-item {
-      position: relative;
-      margin-bottom: 24px;
-
-      .milestone-dot {
-        position: absolute;
-        left: -48px;
-        top: 4px;
-        width: 16px;
-        height: 16px;
-        background-color: $text-light;
-        border-radius: 50%;
-        border: 2px solid white;
-        box-shadow: 0 0 0 2px $border-color;
-        transition: $transition;
-
-        &.completed {
-          background-color: $success-color;
-          box-shadow: 0 0 0 2px #e8f5e9;
-        }
-      }
-
-      .milestone-line {
-        position: absolute;
-        left: -40px;
-        top: 20px;
-        bottom: -24px;
-        width: 2px;
-        background-color: $border-color;
-        transition: $transition;
-
-        &.completed {
-          background-color: $success-color;
-        }
-      }
-
-      .milestone-content {
-        padding: 16px;
-        background-color: $bg-white;
-        border-radius: 8px;
-        border-left: 3px solid $border-color;
-        transition: $transition;
-
-        &:hover {
-          box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
-        }
-      }
-
-      .milestone-header {
-        display: flex;
-        justify-content: space-between;
-        align-items: center;
-        margin-bottom: 8px;
-
-        .milestone-title {
-          font-size: 16px;
-          font-weight: 600;
-          color: $text-primary;
-          margin: 0;
-        }
-
-        .milestone-status {
-          padding: 4px 12px;
-          border-radius: 16px;
-          font-size: 12px;
-          font-weight: 500;
-
-          &.status-completed {
-            color: $success-color;
-            background-color: #e8f5e9;
-          }
-
-          &.status-pending {
-            color: $warning-color;
-            background-color: #fff3e0;
-          }
-        }
-      }
-
-      .milestone-description {
-        font-size: 14px;
-        color: $text-secondary;
-        line-height: 1.5;
-        margin: 0 0 12px 0;
-      }
-
-      .milestone-dates {
-        display: flex;
-        gap: 24px;
-        margin-bottom: 12px;
-
-        .date-item {
-          display: flex;
-          flex-direction: column;
-
-          label {
-            font-size: 11px;
-            color: $text-light;
-            margin-bottom: 2px;
-          }
-
-          span {
-            font-size: 13px;
-            color: $text-primary;
-            font-weight: 500;
-          }
-        }
-      }
-
-      .milestone-actions {
-        display: flex;
-        gap: 8px;
-      }
-    }
-
-    .milestone-item:last-child {
-      margin-bottom: 0;
-
-      .milestone-line {
-        display: none;
-      }
-    }
-  }
-
-  // 任务过滤和搜索
-  .tasks-filter,
-  .files-filter {
-    display: flex;
-    justify-content: space-between;
-    align-items: center;
-    margin-bottom: 24px;
-
-    .filter-options {
-      display: flex;
-      gap: 8px;
-
-      .filter-btn {
-        padding: 6px 16px;
-        border: 1px solid $border-color;
-        background-color: $bg-white;
-        color: $text-secondary;
-        font-size: 13px;
-        font-weight: 500;
-        cursor: pointer;
-        border-radius: 4px;
-        transition: $transition;
-
-        &:hover {
-          color: $primary-color;
-          border-color: $primary-color;
-        }
-
-        &.active {
-          color: white;
-          background-color: $primary-color;
-          border-color: $primary-color;
-        }
-      }
-    }
-
-    .search-box {
-      position: relative;
-      width: 240px;
-
-      svg {
-        position: absolute;
-        left: 12px;
-        top: 50%;
-        transform: translateY(-50%);
-        color: $text-light;
-      }
-
-      input {
-        width: 100%;
-        padding: 8px 12px 8px 40px;
-        border: 1px solid $border-color;
-        border-radius: 4px;
-        font-size: 14px;
-        color: $text-primary;
-        transition: $transition;
-
-        &:focus {
-          outline: none;
-          border-color: $primary-color;
-          box-shadow: 0 0 0 2px rgba(30, 136, 229, 0.1);
-        }
-
-        &::placeholder {
-          color: $text-light;
-        }
-      }
-    }
-  }
-
-  // 任务列表
-  .tasks-list {
-    .task-item {
-      display: flex;
-      gap: 16px;
-      padding: 16px;
-      margin-bottom: 12px;
-      background-color: $bg-white;
-      border-radius: 8px;
-      border-left: 3px solid $border-color;
-      transition: $transition;
-
-      &:hover {
-        box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
-      }
-
-      .task-checkbox {
-        flex-shrink: 0;
-
-        input[type="checkbox"] {
-          width: 18px;
-          height: 18px;
-          cursor: pointer;
-        }
-      }
-
-      .task-content {
-        flex: 1;
-
-        .task-title {
-          font-size: 16px;
-          font-weight: 600;
-          color: $text-primary;
-          margin: 0 0 8px 0;
-          transition: $transition;
-
-          &.completed {
-            color: $text-light;
-            text-decoration: line-through;
-          }
-        }
-
-        .task-description {
-          font-size: 14px;
-          color: $text-secondary;
-          line-height: 1.5;
-          margin: 0 0 12px 0;
-        }
-
-        .task-meta {
-          display: flex;
-          gap: 24px;
-
-          span {
-            display: flex;
-            align-items: center;
-            gap: 6px;
-            font-size: 13px;
-            color: $text-secondary;
-          }
-
-          .task-deadline {
-            &.overdue {
-              color: $danger-color;
-            }
-          }
-
-          .task-priority {
-            padding: 2px 8px;
-            border-radius: 12px;
-            font-size: 11px;
-            font-weight: 500;
-
-            &.priority-high {
-              color: $danger-color;
-              background-color: #ffebee;
-            }
-
-            &.priority-medium {
-              color: $warning-color;
-              background-color: #fff3e0;
-            }
-
-            &.priority-low {
-              color: $success-color;
-              background-color: #e8f5e9;
-            }
-          }
-        }
-      }
-    }
-  }
-
-  // 消息容器
-  .messages-container {
-    display: flex;
-    flex-direction: column;
-    height: 500px;
-
-    .messages-list {
-      flex: 1;
-      overflow-y: auto;
-      padding-right: 8px;
-      margin-bottom: 16px;
-
-      .message-item {
-        display: flex;
-        gap: 12px;
-        margin-bottom: 24px;
-
-        .message-avatar {
-          display: flex;
-          align-items: center;
-          justify-content: center;
-          width: 36px;
-          height: 36px;
-          font-size: 14px;
-          font-weight: 600;
-          color: white;
-          background-color: $primary-color;
-          border-radius: 50%;
-          flex-shrink: 0;
-        }
-
-        .message-content {
-          max-width: 70%;
-
-          .message-header {
-            display: flex;
-            justify-content: space-between;
-            align-items: center;
-            margin-bottom: 4px;
-
-            .message-sender {
-              font-size: 13px;
-              font-weight: 600;
-              color: $text-primary;
-            }
-
-            .message-time {
-              font-size: 11px;
-              color: $text-light;
-            }
-          }
-
-          .message-text {
-            font-size: 14px;
-            color: $text-secondary;
-            line-height: 1.5;
-            padding: 12px;
-            background-color: $bg-light;
-            border-radius: 12px;
-          }
-        }
-      }
-    }
-
-    .message-input-area {
-      .message-actions {
-        display: flex;
-        justify-content: flex-end;
-        gap: 12px;
-        margin-top: 12px;
-      }
-
-      textarea {
-        width: 100%;
-        padding: 12px;
-        border: 1px solid $border-color;
-        border-radius: 6px;
-        font-size: 14px;
-        color: $text-primary;
-        resize: vertical;
-        transition: $transition;
-
-        &:focus {
-          outline: none;
-          border-color: $primary-color;
-          box-shadow: 0 0 0 2px rgba(30, 136, 229, 0.1);
-        }
-
-        &::placeholder {
-          color: $text-light;
-        }
-      }
-    }
-  }
-
-  // 文件列表
-  .files-list {
-    .file-item {
-      display: flex;
-      align-items: center;
-      gap: 16px;
-      padding: 16px;
-      margin-bottom: 12px;
-      background-color: $bg-white;
-      border-radius: 8px;
-      transition: $transition;
-
-      &:hover {
-        box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
-      }
-
-      .file-icon {
-        display: flex;
-        align-items: center;
-        justify-content: center;
-        width: 48px;
-        height: 48px;
-        border-radius: 8px;
-        flex-shrink: 0;
-
-        &.type-document {
-          color: $primary-color;
-          background-color: #e3f2fd;
-        }
-
-        &.type-image {
-          color: $success-color;
-          background-color: #e8f5e9;
-        }
-
-        &.type-spreadsheet {
-          color: $warning-color;
-          background-color: #fff3e0;
-        }
-      }
-
-      .file-info {
-        flex: 1;
-
-        .file-name {
-          font-size: 14px;
-          font-weight: 500;
-          color: $text-primary;
-          margin: 0 0 8px 0;
-        }
-
-        .file-meta {
-          display: flex;
-          gap: 16px;
-
-          span {
-            font-size: 12px;
-            color: $text-light;
-          }
-        }
-      }
-
-      .file-actions {
-        display: flex;
-        gap: 8px;
-
-        .action-btn {
-          display: flex;
-          align-items: center;
-          justify-content: center;
-          width: 36px;
-          height: 36px;
-          border: 1px solid $border-color;
-          background-color: $bg-white;
-          color: $text-secondary;
-          border-radius: 6px;
-          cursor: pointer;
-          transition: $transition;
-
-          &:hover {
-            color: $primary-color;
-            border-color: $primary-color;
-          }
-        }
-      }
-    }
-  }
-}
-
-// iOS风格按钮点击效果
-.button {
-  &:active {
-    transform: scale(0.96);
-    transition: transform 0.1s ease;
-  }
-  
-  &-primary:active {
-    background: #0E42CB;
-  }
-  
-  &-secondary:active {
-    background: #D1D1D6;
-  }
-}
-
-// 卡片悬停效果
-.card {
-  transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
-  
-  &:hover {
-    transform: translateY(-2px);
-    box-shadow: $shadow-md;
-  }
-}
-
-// 响应式设计
-@media (max-width: 1024px) {
-  .overview-grid {
-    grid-template-columns: 1fr;
-  }
-
-  .project-content {
-    padding: 16px;
-  }
-
-  .sidebar {
-    width: 200px;
-  }
-}
-
-@media (max-width: 768px) {
-  .top-navbar {
-    padding: 0 16px;
-
-    .navbar-right {
-      gap: 8px;
-    }
-
-    .user-name {
-      display: none;
-    }
-  }
-
-  .sidebar {
-    display: none;
-  }
-
-  .project-header {
-    flex-direction: column;
-    gap: 16px;
-    align-items: flex-start !important;
-  }
-
-  .project-actions {
-    width: 100%;
-    justify-content: flex-start;
-  }
-
-  .tab-header {
-    flex-wrap: wrap;
-
-    .tab-btn {
-      flex: 1;
-      min-width: 120px;
-    }
-  }
-
-  .tasks-filter,
-  .files-filter {
-    flex-direction: column;
-    gap: 16px;
-    align-items: flex-start !important;
-
-    .search-box {
-      width: 100%;
-    }
-  }
-
-  .task-meta,
-  .file-meta {
-    flex-wrap: wrap;
-    gap: 12px !important;
-  }
-
-  .messages-container {
-    height: 400px;
-  }
-
-  .message-content {
-    max-width: 85% !important;
-  }
-}
-
-// Project Detail iOS tweaks
-.project-detail-container {
-  // 统一卡片间距(仅作用于本页)
-  .card {
-    padding: 20px;
-    margin-bottom: 24px;
-  }
-
-  // 标签页激活态(底部指示条 + 主色高亮)
-  .project-tabs {
-    .tab-header {
-      position: relative;
-    }
-    .tab-btn {
-      position: relative;
-    }
-    .tab-btn.active {
-      color: $primary-color;
-    }
-    .tab-btn.active::after {
-      content: '';
-      position: absolute;
-      left: 16px;
-      right: 16px;
-      bottom: -1px;
-      height: 2px;
-      background: $primary-color;
-      border-radius: 2px;
-    }
-  }
-
-  // 进度条填充质感(渐变 + 轻微内阴影)
-  .progress-bar {
-    .progress-fill {
-      background: linear-gradient(90deg, color.adjust($primary-color, $lightness: 8%), $primary-color);
-      border-radius: 4px;
-      box-shadow: inset 0 0 2px rgba(0,0,0,0.08);
-    }
-  }
-
-  // 输入框聚焦的环形高亮
-  .wechat-input-area {
-    .wechat-input:focus {
-      box-shadow: 0 0 0 3px color-mix(in srgb, $primary-color 15%, transparent);
-    }
-  }
-}
-
-// 响应式优化:侧栏在中等屏略窄,小屏隐藏
-@media (max-width: 1200px) {
-  .project-detail-container {
-    .wechat-sidebar { width: 300px; }
-  }
-}
-@media (max-width: 992px) {
-  .project-detail-container {
-    .wechat-sidebar { display: none; }
-    .project-content-main { width: 100%; }
-  }
-}
-
-// Project Detail iOS segmented & refinements (ABCD)
-.project-detail-container {
-  // A+B: 胶囊分段标签 + 细化激活态
-  .project-tabs.ios-tabs {
-    background: $background-primary;
-    border-radius: $border-radius;
-    box-shadow: $shadow-sm;
-    padding: 8px;
-
-    .tab-header {
-      background: $background-secondary;
-      border: 1px solid $border-color;
-      border-radius: $border-radius;
-      padding: 4px;
-      gap: 4px;
-    }
-
-    .tab-btn {
-      position: relative;
-      border-radius: $border-radius;
-      padding: 10px 14px;
-      font-size: 14px;
-      font-weight: 500;
-      color: $text-secondary;
-      transition: color .18s ease, background-color .18s ease, transform .18s ease;
-
-      &:hover { background-color: color-mix(in srgb, $primary-color 6%, transparent); }
-      &:active { transform: scale(0.98); }
-
-      &.active {
-        color: $primary-color;
-        font-weight: 600; // B: 激活时略增字重
-      }
-      &.active::before { // A: 胶囊式激活背景
-        content: '';
-        position: absolute;
-        inset: 0;
-        background: $background-primary;
-        border-radius: inherit;
-        box-shadow: 0 1px 2px rgba(0,0,0,0.06);
-        z-index: -1;
-      }
-      &.active::after { // B: 极细底线增强层次
-        content: '';
-        position: absolute;
-        left: 12px;
-        right: 12px;
-        bottom: 6px;
-        height: 2px;
-        border-radius: 2px;
-        background: $primary-color;
-        opacity: .7;
-      }
-    }
-  }
-
-  // C: 输入区细化
-  .wechat-input-area {
-    .wechat-input::placeholder { color: $text-tertiary; }
-
-    .action-btn:hover {
-      background-color: color-mix(in srgb, $text-primary 8%, transparent);
-      color: $text-primary;
-    }
-
-    .send-btn {
-      &:active:not(:disabled) { transform: scale(0.98); }
-      &:disabled {
-        background-color: $background-tertiary;
-        color: $text-tertiary;
-        border: 1px solid $border-color;
-        opacity: 0.9;
-        pointer-events: none;
-      }
-    }
-  }
-
-  // D: 侧栏与卡片阴影/圆角统一
-  .wechat-sidebar { box-shadow: $shadow-sm; }
-}
-
-@media (max-width: 768px) {
-  .project-detail-container {
-    .wechat-sidebar { border-radius: $border-radius; }
-  }
-}
-
-// Project Detail iOS buttons + history + bottom actions
-.project-detail-container {
-  // 顶部右侧两个按钮:导出报告(secondary)、联系客户(primary)
-  .header-actions {
-    .primary-btn,
-    .secondary-btn {
-      height: 36px;
-      padding: 0 14px;
-      border-radius: 10px;
-      font-size: 14px;
-      font-weight: 500;
-      letter-spacing: .2px;
-      display: inline-flex;
-      align-items: center;
-      gap: 8px;
-      transition: background-color .18s ease, color .18s ease, transform .12s ease;
-
-      svg { width: 16px; height: 16px; }
-
-      &:active { transform: scale(0.98); }
-    }
-
-    .secondary-btn {
-      color: $text-primary;
-      background-color: $bg-white;
-      border: 1px solid $border-color;
-
-      &:hover { background-color: $bg-light; }
-      &:focus-visible { box-shadow: 0 0 0 3px color-mix(in srgb, $text-primary 10%, transparent); }
-    }
-
-    .primary-btn {
-      color: #fff;
-      background-color: $primary-color;
-      border: 1px solid $primary-color;
-
-      &:hover { background-color: color.adjust($primary-color, $lightness: -6%); }
-      &:focus-visible { box-shadow: 0 0 0 3px color-mix(in srgb, $primary-color 20%, transparent); }
-      &:disabled { background: $text-tertiary; border-color: $text-tertiary; cursor: not-allowed; }
-    }
-  }
-
-  // 历史服务记录板块字体与排版
-  .historical-records-card {
-    .record-section {
-      .projects-list { display: grid; grid-template-columns: 1fr; gap: 10px; }
-      .project-item { padding: 10px 12px; border-radius: 8px; background: $bg-white; border: 1px solid $border-color; }
-      .project-name { font-size: 14px; font-weight: 600; color: $text-primary; margin-bottom: 4px; }
-      .project-period { font-size: 12px; color: $text-tertiary; margin-bottom: 6px; }
-      .project-description { font-size: 14px; color: $text-primary; line-height: 1.6; margin-bottom: 4px; }
-      .project-status { font-size: 12px; color: $text-secondary; }
-    }
-  }
-
-  // 底部售后操作栏(固定在底部的小界面):现代感与极简
-  .after-sales-actions.ios-actions {
-    position: sticky;
-    bottom: 0;
-    z-index: 5;
-    padding: 12px 0;
-
-    .actions-container {
-      display: flex;
-      gap: 12px;
-      justify-content: center;
-      padding: 10px 16px;
-      background: color-mix(in srgb, $bg-white 70%, transparent);
-      border-top: 1px solid $border-color;
-      backdrop-filter: saturate(160%) blur(10px);
-      -webkit-backdrop-filter: saturate(160%) blur(10px);
-      box-shadow: 0 -2px 8px rgba(0,0,0,0.04);
-      border-radius: 12px;
-    }
-
-    .primary-btn,
-    .secondary-btn,
-    .warning-btn {
-      height: 36px;
-      padding: 0 14px;
-      border-radius: 10px;
-      font-size: 14px;
-      font-weight: 500;
-      letter-spacing: .2px;
-      border: 1px solid transparent;
-      transition: background-color .18s ease, color .18s ease, transform .12s ease;
-      &:active { transform: scale(0.98); }
-    }
-
-    .primary-btn { color: #fff; background: $primary-color; border-color: $primary-color; &:hover { background: color.adjust($primary-color, $lightness: -6%); } }
-    .secondary-btn { color: $text-primary; background: $bg-white; border-color: $border-color; &:hover { background: $bg-light; } }
-    .warning-btn { color: #8a6100; background: #fff8e1; border-color: #ffe0a5; &:hover { background: #ffefc2; } }
-  }
-
-  // 里程碑小界面下方的动作区
-  .milestone-actions {
-    display: flex;
-    gap: 8px;
-    padding-top: 8px;
-    border-top: 1px dashed $border-color;
-
-    .primary-btn.small {
-      border-radius: 10px;
-      padding: 6px 12px;
-      font-weight: 600;
-    }
-  }
-}
-
-// 移动端微调
-@media (max-width: 768px) {
-  .project-detail-container {
-    .after-sales-actions.ios-actions .actions-container { border-radius: 10px; gap: 8px; }
-    .historical-records-card .record-section .consultation-item { padding: 10px; }
-  }
-}
-
-// iOS micro tuning — apply all requested adjustments
-.project-detail-container {
-  // Header actions buttons
-  .header-actions {
-    .primary-btn,
-    .secondary-btn {
-      height: 40px;
-      padding: 0 16px;
-      border-radius: 12px;
-    }
-    .primary-btn:hover {
-      background-color: color.adjust($primary-color, $lightness: -4%);
-    }
-  }
-
-  // Historical records cards — softer borders
-  .historical-records-card .record-section {
-    .consultation-item,
-    .cooperation-item,
-    .feedback-item,
-    .project-item {
-      border-color: color-mix(in srgb, $border-color 70%, transparent);
-    }
-  }
-
-  // Bottom sticky actions — lighter frosted glass and rounded container
-  .after-sales-actions.ios-actions {
-    .actions-container {
-      background: color-mix(in srgb, $bg-white 60%, transparent);
-      backdrop-filter: saturate(160%) blur(8px);
-      -webkit-backdrop-filter: saturate(160%) blur(8px);
-      border-radius: 14px;
-    }
-    .warning-btn {
-      color: #7a5200;
-      background: #fff5d6;
-      border-color: #ffd88a;
-      &:hover { background: #ffedbd; }
-    }
-  }
-
-  // Milestone small actions
-  .milestone-actions .primary-btn.small {
-    border-radius: 12px;
-    font-size: 13px;
-  }
-}
-
-@media (max-width: 768px) {
-  .project-detail-container {
-    .header-actions {
-      .primary-btn,
-      .secondary-btn { height: 38px; padding: 0 14px; }
-    }
-    .after-sales-actions.ios-actions .actions-container { border-radius: 12px; }
-  }
-}
-
-// Override: 历史卡片改为无描边 + 极浅阴影(高优先级覆盖)
-.project-detail-container {
-  .historical-records-card .record-section {
-    .consultation-item,
-    .cooperation-item,
-    .feedback-item,
-    .project-item {
-      border: none !important;
-      box-shadow: 0 1px 3px rgba(0,0,0,0.04);
-    }
-  }
-}
-
-@media (max-width: 768px) {
-  .project-detail-container {
-    .historical-records-card .record-section {
-      .consultation-item,
-      .cooperation-item,
-      .feedback-item,
-      .project-item {
-        box-shadow: 0 1px 2px rgba(0,0,0,0.04);
-      }
-    }
-  }
-}
-
-// Typography & layout improvements for Historical Records — 更直观的数据层级
-.project-detail-container {
-  .historical-records-card {
-    .record-section {
-      h4 { font-size: 16px; font-weight: 600; color: $text-primary; margin-bottom: 10px; }
-
-      // 过往咨询记录:日期-内容-状态 三段式布局
-      .consultation-list {
-        .consultation-item {
-          display: grid;
-          grid-template-columns: 128px 1fr auto; // 日期 | 内容 | 状态
-          gap: 8px 12px;
-          align-items: center;
-
-          .consultation-date {
-            font-size: 12px;
-            color: $text-tertiary;
-            letter-spacing: .2px;
-            font-variant-numeric: tabular-nums;
-          }
-          .consultation-content {
-            font-size: 14px;
-            color: $text-primary;
-            line-height: 1.6;
-          }
-          .consultation-status {
-            justify-self: end;
-            font-size: 12px;
-            font-weight: 600;
-            color: $text-primary;
-            background: $bg-light;
-            padding: 2px 8px;
-            border-radius: 10px;
-
-            // 三色状态:成功/处理中/待处理
-            &.status-processed { color: $success-color; background-color: #e8f5e9; }
-            &.status-processing { color: $primary-color; background-color: #e3f2fd; }
-            &.status-pending { color: $warning-color; background-color: #fff3e0; }
-          }
-        }
-      }
-
-      // 合作项目:名称/周期/描述/状态的网格分区,突出名称与状态
-      .projects-list {
-        .project-item {
-          display: grid;
-          grid-template-columns: 1fr auto;
-          grid-template-areas:
-            'name status'
-            'period status'
-            'desc desc';
-          row-gap: 4px;
-          column-gap: 12px;
-
-          .project-name { grid-area: name; font-size: 15px; font-weight: 600; color: $text-primary; }
-          .project-period { grid-area: period; font-size: 12px; color: $text-tertiary; font-variant-numeric: tabular-nums; }
-          .project-description { grid-area: desc; font-size: 14px; color: $text-secondary; line-height: 1.6; }
-          .project-status { grid-area: status; align-self: start; justify-self: end; font-size: 12px; font-weight: 600; padding: 2px 8px; border-radius: 10px; background: #f2f4f7; color: $text-secondary; }
-        }
-      }
-
-      // 历史反馈:日期等数值统一用等宽数字,正文稍加对比度
-      .feedback-list {
-        .feedback-item {
-          .feedback-date { font-variant-numeric: tabular-nums; letter-spacing: .2px; color: $text-light; }
-          .feedback-content { color: $text-primary; line-height: 1.7; }
-          .rating-number { font-variant-numeric: tabular-nums; }
-        }
-      }
-    }
-  }
-}
-
-// 响应式:在移动端收敛为纵向信息流
-@media (max-width: 768px) {
-  .project-detail-container {
-    .historical-records-card {
-      .record-section {
-        .consultation-list .consultation-item {
-          grid-template-columns: 1fr; // 纵向排列:内容 -> 状态 -> 日期
-          .consultation-status { justify-self: start; margin-top: 4px; }
-          .consultation-date { margin-top: 2px; }
-        }
-        .projects-list .project-item {
-          grid-template-columns: 1fr; grid-template-areas: 'name' 'status' 'period' 'desc';
-          .project-status { justify-self: start; margin: 2px 0 6px; }
-        }
-      }
-    }
-  }
-}
-
-/* 只读渲染图预览弹窗 + 缩略图样式(customer-service/project-detail) */
-.project-detail-container {
-  .modal-backdrop {
-    position: fixed;
-    inset: 0;
-    background: rgba(0,0,0,0.45);
-    backdrop-filter: saturate(160%) blur(6px);
-    -webkit-backdrop-filter: saturate(160%) blur(6px);
-    z-index: 999;
-  }
-
-  .modal {
-    position: fixed;
-    top: 50%;
-    left: 50%;
-    transform: translate(-50%, -50%);
-    width: min(920px, 92vw);
-    max-height: 80vh;
-    background: $bg-white;
-    border: 1px solid $border-color;
-    border-radius: $border-radius;
-    box-shadow: 0 12px 28px rgba(0,0,0,0.15);
-    display: flex;
-    flex-direction: column;
-    z-index: 1000;
-  }
-
-  .modal-header {
-    padding: 14px 16px;
-    border-bottom: 1px solid $border-color;
-    display: flex;
-    align-items: center;
-    justify-content: space-between;
-
-    h3 {
-      margin: 0;
-      font-size: 16px;
-      font-weight: 600;
-      color: $text-primary;
-    }
-  }
-
-  .modal-body {
-    padding: 16px;
-    overflow: auto;
-    background: $bg-white;
-  }
-
-  .modal-footer {
-    padding: 12px 16px;
-    border-top: 1px solid $border-color;
-    display: flex;
-    justify-content: flex-end;
-    gap: 8px;
-    background: $bg-white;
-  }
-
-  .close-button {
-    background: transparent;
-    border: none;
-    font-size: 18px;
-    line-height: 1;
-    cursor: pointer;
-    color: $text-secondary;
-
-    &:hover { color: $text-primary; }
-  }
-
-  /* 缩略图网格 */
-  .thumb-list {
-    display: grid;
-    grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
-    gap: 12px;
-  }
-
-  .thumb-item {
-    border: 1px solid $border-color;
-    border-radius: $border-radius;
-    overflow: hidden;
-    background: $bg-white;
-    box-shadow: 0 1px 2px rgba(0,0,0,0.04);
-  }
-
-  .thumb-item img {
-    width: 100%;
-    height: 120px;
-    object-fit: cover;
-    display: block;
-  }
-
-  .thumb-meta {
-    padding: 8px;
-
-    .name {
-      font-size: 13px;
-      color: $text-primary;
-      white-space: nowrap;
-      overflow: hidden;
-      text-overflow: ellipsis;
-    }
-
-    .sub {
-      font-size: 12px;
-      color: $text-secondary;
-      margin-top: 2px;
-    }
-  }
-}

+ 0 - 851
src/app/pages/customer-service/project-detail/project-detail.ts

@@ -1,851 +0,0 @@
-import { Component, OnInit, signal, computed, ViewChild, AfterViewChecked } from '@angular/core';
-import { CommonModule } from '@angular/common';
-import { FormsModule, ReactiveFormsModule } from '@angular/forms';
-import { RouterModule, ActivatedRoute } from '@angular/router';
-import { MatDialog, MatDialogModule } from '@angular/material/dialog';
-import { ProjectService } from '../../../services/project.service';
-import { Project, Task, Message, FileItem, CustomerFeedback, Milestone } from '../../../models/project.model';
-import { ModificationRequestDialog } from './modification-request-dialog';
-import { ComplaintWarningDialog } from './complaint-warning-dialog';
-import { RefundRequestDialog } from './refund-request-dialog';
-
-// 定义项目阶段接口
-interface ProjectStage {
-  name: string;
-  completed: boolean;
-  inProgress: boolean;
-  startDate?: Date;
-  endDate?: Date;
-  responsible?: string;
-  details?: string;
-}
-
-// 定义企业微信消息接口
-interface WechatMessage {
-  sender: string;
-  content: string;
-  timestamp: Date;
-}
-
-// 定义历史咨询记录接口
-interface ConsultationRecord {
-  id: string;
-  date: Date;
-  content: string;
-  status: string;
-}
-
-// 定义合作项目接口
-interface CooperationProject {
-  id: string;
-  name: string;
-  startDate: Date;
-  endDate?: Date;
-  status: string;
-  description: string;
-}
-
-// 定义历史反馈接口
-interface HistoricalFeedback {
-  id: string;
-  date: Date;
-  content: string;
-  rating: number;
-  response?: string;
-}
-
-@Component({
-  selector: 'app-project-detail',
-  standalone: true,
-  imports: [CommonModule, FormsModule, ReactiveFormsModule, RouterModule, MatDialogModule],
-  templateUrl: './project-detail.html',
-  styleUrls: ['./project-detail.scss', '../customer-service-styles.scss']
-})
-export class ProjectDetail implements OnInit, AfterViewChecked {
-  // 项目ID
-  projectId = '';
-  
-  // 项目详情
-  project = signal<Project | null>(null);
-  
-  // 添加 toggleSidebar 方法
-  toggleSidebar(): void {
-    // 这里可以实现侧边栏切换逻辑
-    // 如果需要与全局状态交互,可以注入相应的服务
-    console.log('Toggle sidebar');
-  }
-  
-  // 项目里程碑
-  milestones = signal<Milestone[]>([]);
-  
-  // 项目任务
-  tasks = signal<Task[]>([]);
-  
-  // 项目消息
-  messages = signal<Message[]>([]);
-  
-  // 项目文件
-  files = signal<FileItem[]>([]);
-  
-  // 客户反馈
-  feedbacks = signal<CustomerFeedback[]>([]);
-  
-  // 当前激活的标签页
-  activeTab = signal('overview');
-  
-  // 新消息内容
-  newMessage = signal('');
-  
-  // 项目阶段数据 - 进度时间轴
-  projectStages: ProjectStage[] = [
-    {      
-      name: '需求沟通',
-      completed: true,
-      inProgress: false,
-      startDate: new Date('2023-06-01'),
-      endDate: new Date('2023-06-08'),
-      responsible: '客服小李',
-      details: '完成客户需求确认、现场量房和初步方案讨论'
-    },
-    {
-      name: '建模',
-      completed: true,
-      inProgress: false,
-      startDate: new Date('2023-06-09'),
-      endDate: new Date('2023-06-18'),
-      responsible: '张设计师',
-      details: '创建3D模型和基础渲染'
-    },
-    {
-      name: '软装',
-      completed: false,
-      inProgress: true,
-      startDate: new Date('2023-06-19'),
-      responsible: '张设计师',
-      details: '选择和搭配家具、灯具和装饰品'
-    },
-    {
-      name: '渲染',
-      completed: false,
-      inProgress: false,
-      startDate: new Date('2023-06-26'),
-      responsible: '张设计师',
-      details: '生成最终渲染图'
-    },
-    {
-      name: '后期',
-      completed: false,
-      inProgress: false,
-      startDate: new Date('2023-07-03'),
-      responsible: '张设计师',
-      details: '后期图像处理和优化'
-    },
-    {
-      name: '投诉处理',
-      completed: false,
-      inProgress: false,
-      startDate: new Date('2023-07-10'),
-      endDate: new Date('2023-07-15'),
-      responsible: '客服小李',
-      details: '项目验收、交付和投诉处理'
-    }
-  ];
-  
-  // 企业微信聊天相关
-  @ViewChild('wechatMessages') wechatMessagesContainer: any;
-  wechatMessagesList: WechatMessage[] = [];
-  wechatInput = '';
-  scrollToBottom = false;
-  
-  // 历史服务记录相关
-  consultationRecords = signal<ConsultationRecord[]>([]);
-  cooperationProjects = signal<CooperationProject[]>([]);
-  historicalFeedbacks = signal<HistoricalFeedback[]>([]);
-  
-  // 售后处理弹窗状态
-  showModificationRequest = false;
-  showComplaintWarning = false;
-  showRefundRequest = false;
-  
-  // 渲染图只读预览弹窗状态与数据
-  showRenderPreviewModal = false;
-  renderImages = computed(() => this.files().filter(f => f.type === 'image'));
-  
-  // 项目状态颜色映射
-  statusColors = {
-    '进行中': 'primary',
-    '待确认': 'warning',
-    '已完成': 'success',
-    '已暂停': 'secondary',
-    '已取消': 'danger'
-  };
-  
-  // 计算完成进度
-  completionProgress = computed(() => {
-    if (!this.tasks().length) return 0;
-    const completedTasks = this.tasks().filter(task => task.isCompleted).length;
-    return Math.round((completedTasks / this.tasks().length) * 100);
-  });
-  
-  constructor(
-    private route: ActivatedRoute,
-    private projectService: ProjectService,
-    private dialog: MatDialog
-  ) {
-    // 获取路由参数中的项目ID
-    this.route.paramMap.subscribe(params => {
-      this.projectId = params.get('id') || '';
-    });
-  }
-  
-  ngOnInit(): void {
-    this.loadProjectDetails();
-  }
-  
-  // 加载项目详情
-  loadProjectDetails(): void {
-    // 模拟从服务获取项目数据
-    this.projectService.getProjectById(this.projectId).subscribe(project => {
-      if (project) {
-        this.project.set(project);
-      }
-    });
-    
-    // 加载模拟数据
-    this.loadMockData();
-  }
-  
-  // 加载模拟数据
-  // 修复 loadMockData 方法中的任务对象,确保类型一致性
-  loadMockData(): void {
-    // 初始化企业微信聊天
-    this.initWechatMessages();
-    
-    // 初始化历史服务记录
-    this.initHistoricalServiceRecords();
-    
-    // 模拟里程碑数据
-    this.milestones.set([
-      { 
-        id: 'm1',
-        title: '客户需求确认',
-        description: '确认客户的装修需求和风格偏好',
-        dueDate: new Date('2023-06-10'),
-        completedDate: new Date('2023-06-08'),
-        isCompleted: true
-      },
-      {
-        id: 'm2',
-        title: '设计方案提交',
-        description: '提交初步设计方案供客户审阅',
-        dueDate: new Date('2023-06-20'),
-        completedDate: new Date('2023-06-18'),
-        isCompleted: true
-      },
-      {
-        id: 'm3',
-        title: '方案修改与确认',
-        description: '根据客户反馈修改并最终确认方案',
-        dueDate: new Date('2023-06-25'),
-        completedDate: undefined,
-        isCompleted: false
-      },
-      {
-        id: 'm4',
-        title: '施工图制作',
-        description: '制作详细的施工图纸',
-        dueDate: new Date('2023-07-05'),
-        completedDate: undefined,
-        isCompleted: false
-      },
-      {
-        id: 'm5',
-        title: '项目验收',
-        description: '客户验收最终成果',
-        dueDate: new Date('2023-07-15'),
-        completedDate: undefined,
-        isCompleted: false
-      }
-    ]);
-    
-    // 模拟任务数据 - 确保所有任务对象都有完整的必填属性
-    this.tasks.set([
-      { 
-        id: 't1',
-        title: '现场量房',
-        description: '前往客户现场进行房屋测量',
-        projectId: this.projectId,
-        projectName: '现代简约风格三居室设计',
-        assignee: '张设计师',
-        deadline: new Date('2023-06-05'),
-        completedDate: new Date('2023-06-04'),
-        isCompleted: true,
-        isOverdue: false,
-        priority: 'high',
-        stage: '需求沟通' // 添加 stage 属性,确保符合 Task 接口
-      },
-      {
-        id: 't2',
-        title: '设计初稿',
-        description: '完成设计方案初稿',
-        projectId: this.projectId,
-        projectName: '现代简约风格三居室设计',
-        assignee: '张设计师',
-        deadline: new Date('2023-06-15'),
-        completedDate: new Date('2023-06-14'),
-        isCompleted: true,
-        isOverdue: false,
-        priority: 'high',
-        stage: '建模'
-      },
-      {
-        id: 't3',
-        title: '客户沟通',
-        description: '与客户沟通设计方案反馈',
-        projectId: this.projectId,
-        projectName: '现代简约风格三居室设计',
-        assignee: '客服小李',
-        deadline: new Date('2023-06-22'),
-        completedDate: undefined,
-        isCompleted: false,
-        isOverdue: false,
-        priority: 'medium',
-        stage: '需求沟通'
-      },
-      {
-        id: 't4',
-        title: '方案修改',
-        description: '根据客户反馈修改设计方案',
-        projectId: this.projectId,
-        projectName: '现代简约风格三居室设计',
-        assignee: '张设计师',
-        deadline: new Date('2023-06-28'),
-        completedDate: undefined,
-        isCompleted: false,
-        isOverdue: false,
-        priority: 'high',
-        stage: '软装'
-      },
-      {
-        id: 't5',
-        title: '施工图绘制',
-        description: '绘制详细的施工图纸',
-        projectId: this.projectId,
-        projectName: '现代简约风格三居室设计',
-        assignee: '王设计师',
-        deadline: new Date('2023-07-10'),
-        completedDate: undefined,
-        isCompleted: false,
-        isOverdue: false,
-        priority: 'medium',
-        stage: '渲染'
-      }
-    ]);
-    
-    // 模拟消息数据
-    this.messages.set([
-      {
-        id: 'msg1',
-        sender: '客户王先生',
-        content: '设计方案收到了,整体很满意,客厅的颜色可以再调整一下吗?',
-        timestamp: new Date('2023-06-19T10:30:00'),
-        isRead: true,
-        type: 'text'
-      },
-      {
-        id: 'msg2',
-        sender: '客服小李',
-        content: '好的,我们会根据您的需求调整客厅颜色方案,稍后给您发送修改后的效果图。',
-        timestamp: new Date('2023-06-19T10:45:00'),
-        isRead: true,
-        type: 'text'
-      },
-      {
-        id: 'msg3',
-        sender: '张设计师',
-        content: '修改后的客厅效果图已上传,请查收。',
-        timestamp: new Date('2023-06-19T14:20:00'),
-        isRead: true,
-        type: 'text'
-      },
-      {
-        id: 'msg4',
-        sender: '客户王先生',
-        content: '这个效果很好,就按照这个方案进行吧!',
-        timestamp: new Date('2023-06-20T09:15:00'),
-        isRead: true,
-        type: 'text'
-      },
-      {
-        id: 'msg5',
-        sender: '客服小李',
-        content: '收到,我们会尽快开始下一阶段的工作。',
-        timestamp: new Date('2023-06-20T09:30:00'),
-        isRead: true,
-        type: 'text'
-      }
-    ]);
-    
-    // 模拟文件数据
-    this.files.set([
-      {
-        id: 'file1',
-        name: '设计方案初稿.pdf',
-        type: 'document',
-        size: '2.5MB',
-        url: 'https://example.com/files/design-proposal.pdf',
-        uploadedBy: '张设计师',
-        uploadedAt: new Date('2023-06-15'),
-        downloadCount: 12
-      },
-      {
-        id: 'file2',
-        name: '客厅效果图.png',
-        type: 'image',
-        size: '1.8MB',
-        url: 'https://example.com/files/living-room.png',
-        uploadedBy: '张设计师',
-        uploadedAt: new Date('2023-06-18'),
-        downloadCount: 8
-      },
-      {
-        id: 'file3',
-        name: '修改后客厅效果图.png',
-        type: 'image',
-        size: '2.1MB',
-        url: 'https://example.com/files/living-room-revised.png',
-        uploadedBy: '张设计师',
-        uploadedAt: new Date('2023-06-19'),
-        downloadCount: 5
-      },
-      {
-        id: 'file4',
-        name: '客户需求文档.docx',
-        type: 'document',
-        size: '1.2MB',
-        url: 'https://example.com/files/requirements.docx',
-        uploadedBy: '客服小李',
-        uploadedAt: new Date('2023-06-05'),
-        downloadCount: 10
-      },
-      {
-        id: 'file5',
-        name: '房屋测量数据.xlsx',
-        type: 'spreadsheet',
-        size: '0.8MB',
-        url: 'https://example.com/files/measurement.xlsx',
-        uploadedBy: '张设计师',
-        uploadedAt: new Date('2023-06-04'),
-        downloadCount: 7
-      }
-    ]);
-    
-    // 模拟客户反馈
-    this.feedbacks.set([
-      {
-        id: 'fb1',
-        projectId: this.projectId,
-        customerName: '王先生',
-        content: '对设计方案整体满意,但希望客厅颜色能更柔和一些。',
-        rating: 4,
-        createdAt: new Date('2023-06-19'),
-        status: '已解决',
-        response: '已根据您的需求调整了客厅颜色方案,详见最新上传的效果图。',
-        isSatisfied: true
-      }
-    ]);
-  }
-  
-  // 切换标签页
-  switchTab(tab: string): void {
-    this.activeTab.set(tab);
-  }
-  
-  // 增强版发送消息功能
-  sendMessage(): void {
-    if (this.newMessage().trim()) {
-      // 添加发送动画效果
-      const sendBtn = document.querySelector('.message-actions .primary-btn');
-      if (sendBtn) {
-        sendBtn.classList.add('sending');
-        setTimeout(() => sendBtn.classList.remove('sending'), 300);
-      }
-
-      const newMsg: Message = {
-        id: `msg${Date.now()}`,
-        sender: '客服小李',
-        content: this.newMessage().trim(),
-        timestamp: new Date(),
-        isRead: true,
-        type: 'text'
-      };
-      
-      this.messages.set([...this.messages(), newMsg]);
-      this.newMessage.set('');
-      
-      // 自动滚动到底部
-      setTimeout(() => {
-        const container = document.querySelector('.messages-list');
-        if (container) {
-          container.scrollTop = container.scrollHeight;
-        }
-      }, 100);
-    }
-  }
-  
-  // 增强版完成任务功能
-  completeTask(taskId: string): void {
-    // 添加完成动画效果
-    const taskElement = document.querySelector(`.task-item[data-id="${taskId}"]`);
-    if (taskElement) {
-      taskElement.classList.add('completing');
-      setTimeout(() => taskElement.classList.remove('completing'), 500);
-    }
-
-    this.tasks.set(
-      this.tasks().map(task => 
-        task.id === taskId 
-          ? { ...task, isCompleted: true, completedDate: new Date(), isOverdue: false }
-          : task
-      )
-    );
-    
-    // 播放完成音效
-    this.playSound('complete');
-  }
-  
-  // 修复 completeMilestone 方法中的类型问题
-  completeMilestone(milestoneId: string): void {
-    this.milestones.set(
-      this.milestones().map(milestone => 
-        milestone.id === milestoneId 
-          ? { ...milestone, isCompleted: true, completedDate: new Date() }
-          : milestone
-      )
-    );
-  }
-  
-  // 增强 formatDate 和 formatDateTime 方法的类型安全
-  formatDate(date: Date | string | null | undefined): string {
-    if (!date) return '-';
-    try {
-      return new Date(date).toLocaleDateString('zh-CN', {
-        year: 'numeric',
-        month: '2-digit',
-        day: '2-digit'
-      });
-    } catch (error) {
-      console.error('日期格式化错误:', error);
-      return '-';
-    }
-  }
-  
-  formatDateTime(date: Date | string | null | undefined): string {
-    if (!date) return '-';
-    try {
-      return new Date(date).toLocaleString('zh-CN', {
-        year: 'numeric',
-        month: '2-digit',
-        day: '2-digit',
-        hour: '2-digit',
-        minute: '2-digit'
-      });
-    } catch (error) {
-      console.error('日期时间格式化错误:', error);
-      return '-';
-    }
-  }
-  
-  // 下载文件
-  downloadFile(file: FileItem): void {
-    console.log('下载文件:', file.name);
-    // 实际应用中,这里会处理文件下载逻辑
-  }
-  
-  // 查看文件预览
-  previewFile(file: FileItem): void {
-    console.log('预览文件:', file.name);
-    // 实际应用中,这里会打开文件预览
-  }
-  
-  // 打开渲染图只读预览
-  openRenderPreview(): void {
-    this.showRenderPreviewModal = true;
-  }
-
-  // 关闭渲染图只读预览
-  closeRenderPreview(): void {
-    this.showRenderPreviewModal = false;
-  }
-  
-  // 获取项目状态的CSS类名
-  getProjectStatusClass(status: string | null | undefined): string {
-    if (!status) return 'status-default';
-    switch (status) {
-      case '进行中':
-        return 'status-active';
-      case '待确认':
-        return 'status-pending';
-      case '已完成':
-        return 'status-completed';
-      default:
-        return 'status-default';
-    }
-  }
-  
-  // 获取反馈客户名称(带安全检查)
-  getFeedbackCustomerName(feedback: CustomerFeedback | undefined | null): string {
-    if (!feedback) return '未知客户';
-    return feedback.customerName || '未知客户';
-  }
-  
-  // 获取反馈评分(带安全检查)
-  getFeedbackRating(feedback: CustomerFeedback | undefined | null): number {
-    if (!feedback) return 0;
-    return feedback.rating || 0;
-  }
-  
-  // 消息输入事件处理
-  onMessageInput(event: Event): void {
-    const target = event.target as HTMLTextAreaElement;
-    this.newMessage.set(target.value);
-  }
-  
-  // 添加正确的progressFillWidth计算属性
-  progressFillWidth = computed(() => {
-    return `${this.completionProgress()}%`;
-  });
-  
-  // AfterViewChecked 接口实现,用于滚动到聊天窗口底部
-  ngAfterViewChecked(): void {
-    if (this.scrollToBottom && this.wechatMessagesContainer) {
-      this.wechatMessagesContainer.nativeElement.scrollTop = 
-        this.wechatMessagesContainer.nativeElement.scrollHeight;
-      this.scrollToBottom = false;
-    }
-  }
-  
-  // 初始化企业微信聊天
-  initWechatMessages(): void {
-    // 模拟企业微信聊天消息
-    this.wechatMessagesList = [
-      {
-        sender: '客服小李',
-        content: '您好,张先生,我们已经收到您的需求,正在为您制定设计方案。',
-        timestamp: new Date('2023-06-01T10:30:00')
-      },
-      {
-        sender: '张先生',
-        content: '好的,我希望客厅光线充足,储物空间充足,并且使用环保材料。',
-        timestamp: new Date('2023-06-01T10:35:00')
-      },
-      {
-        sender: '客服小李',
-        content: '明白了,我们会重点考虑这些需求。预计3天内可以完成初步方案。',
-        timestamp: new Date('2023-06-01T10:40:00')
-      },
-      {
-        sender: '张设计师',
-        content: '您好,我是负责您项目的设计师小张。今天下午我会去现场量房,请问您方便吗?',
-        timestamp: new Date('2023-06-02T14:00:00')
-      },
-      {
-        sender: '张先生',
-        content: '下午好的,我在小区门口等您。',
-        timestamp: new Date('2023-06-02T14:05:00')
-      },
-      {
-        sender: '张设计师',
-        content: '已完成量房,正在制作初步设计方案。',
-        timestamp: new Date('2023-06-05T18:30:00')
-      },
-      {
-        sender: '客服小李',
-        content: '张先生,初步设计方案已完成,您什么时候方便查看一下?',
-        timestamp: new Date('2023-06-08T10:00:00')
-      }
-    ];
-    
-    // 确保滚动到底部
-    setTimeout(() => {
-      this.scrollToBottom = true;
-    }, 100);
-  }
-  
-  // 增强版发送企业微信消息
-  sendWechatMessage(): void {
-    if (!this.wechatInput.trim()) return;
-    
-    // 添加发送动画
-    const sendBtn = document.querySelector('.wechat-input-area .send-btn');
-    if (sendBtn) {
-      sendBtn.classList.add('sending');
-      setTimeout(() => sendBtn.classList.remove('sending'), 300);
-    }
-    
-    const newMessage: WechatMessage = {
-      sender: '客服小李',
-      content: this.wechatInput.trim(),
-      timestamp: new Date()
-    };
-    
-    this.wechatMessagesList = [...this.wechatMessagesList, newMessage];
-    this.wechatInput = '';
-    this.scrollToBottom = true;
-    
-    // 播放发送音效
-    this.playSound('message');
-    
-    // 模拟对方回复
-    setTimeout(() => {
-      const replyMessage: WechatMessage = {
-        sender: '张先生',
-        content: '收到,我稍后查看。',
-        timestamp: new Date()
-      };
-      
-      this.wechatMessagesList = [...this.wechatMessagesList, replyMessage];
-      this.scrollToBottom = true;
-      
-      // 播放接收音效
-      this.playSound('notification');
-    }, 2000);
-  }
-
-  // 新增播放音效方法
-  playSound(type: 'message' | 'notification' | 'complete'): void {
-    // 实际项目中这里会播放对应的音效
-    console.log(`播放${type}音效`);
-  }
-  
-  // 初始化历史服务记录
-  initHistoricalServiceRecords(): void {
-    // 模拟过往咨询记录
-    this.consultationRecords.set([
-      {
-        id: 'cons1',
-        date: new Date('2023-01-15'),
-        content: '咨询关于厨房改造的可行性和预算',
-        status: '已解决'
-      },
-      {
-        id: 'cons2',
-        date: new Date('2023-03-20'),
-        content: '询问装修材料环保认证相关问题',
-        status: '已解决'
-      },
-      {
-        id: 'cons3',
-        date: new Date('2023-05-10'),
-        content: '了解装修分期付款方案',
-        status: '已解决'
-      }
-    ]);
-    
-    // 模拟合作项目记录
-    this.cooperationProjects.set([
-      {
-        id: 'proj1',
-        name: '2022年现代简约卧室设计项目',
-        startDate: new Date('2022-08-15'),
-        endDate: new Date('2022-10-30'),
-        status: '已完成',
-        description: '为客户设计并实施了现代简约风格的卧室改造,包括定制衣柜和床头背景墙'
-      },
-      {
-        id: 'proj2',
-        name: '2023年欧式厨房设计项目',
-        startDate: new Date('2023-02-01'),
-        endDate: new Date('2023-04-15'),
-        status: '已完成',
-        description: '设计并安装了全套欧式风格厨房,包括橱柜、台面和电器选型'
-      }
-    ]);
-    
-    // 模拟历史反馈/评价
-    this.historicalFeedbacks.set([
-      {
-        id: 'fb1',
-        date: new Date('2022-11-05'),
-        content: '卧室设计非常满意,空间利用合理,风格符合预期',
-        rating: 5,
-        response: '感谢您的好评,我们会继续努力为您提供优质服务'
-      },
-      {
-        id: 'fb2',
-        date: new Date('2023-04-20'),
-        content: '厨房装修质量很好,但工期比预期稍长',
-        rating: 4,
-        response: '感谢您的反馈,我们已经优化了施工流程,会在后续项目中改进'
-      }
-    ]);
-  }
-  
-  // 格式化为时间显示
-  formatTime(date: Date): string {
-    if (!date) return '';
-    return new Date(date).toLocaleTimeString('zh-CN', { 
-      hour: '2-digit', 
-      minute: '2-digit' 
-    });
-  }
-  
-  // 获取当前日期的计算属性
-  currentDate = computed(() => {
-    return new Date();
-  });
-  
-  // 售后处理入口方法
-  openModificationRequest(): void {
-    const dialogRef = this.dialog.open(ModificationRequestDialog, {
-      width: '500px',
-      data: {
-        projectId: this.projectId,
-        projectName: this.project()?.name || '现代简约风格三居室设计',
-        projectStatus: this.project()?.status || '进行中'
-      }
-    });
-
-    dialogRef.afterClosed().subscribe(result => {
-      if (result) {
-        console.log('申请修改提交成功', result);
-        // 这里可以添加提交成功后的处理逻辑,如刷新数据、显示通知等
-      }
-    });
-  }
-  
-  openComplaintWarning(): void {
-    const dialogRef = this.dialog.open(ComplaintWarningDialog, {
-      width: '500px',
-      data: {
-        projectId: this.projectId,
-        projectName: this.project()?.name || '现代简约风格三居室设计'
-      }
-    });
-
-    dialogRef.afterClosed().subscribe(result => {
-      if (result) {
-        console.log('投诉预警提交成功', result);
-        // 这里可以添加提交成功后的处理逻辑
-      }
-    });
-  }
-  
-  openRefundRequest(): void {
-    const dialogRef = this.dialog.open(RefundRequestDialog, {
-      width: '500px',
-      data: {
-        projectId: this.projectId,
-        projectName: this.project()?.name || '现代简约风格三居室设计',
-        projectAmount: 85000 // 模拟项目金额
-      }
-    });
-
-    dialogRef.afterClosed().subscribe(result => {
-      if (result) {
-        console.log('申请退款提交成功', result);
-        // 这里可以添加提交成功后的处理逻辑
-      }
-    });
-  }
-}

+ 0 - 187
src/app/pages/customer-service/project-detail/refund-request-dialog.ts

@@ -1,187 +0,0 @@
-import { CommonModule } from '@angular/common';
-import { FormsModule } from '@angular/forms';
-import { Component, Inject } from '@angular/core';
-import { MatButtonModule } from '@angular/material/button';
-import { MatDialogModule, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
-import { MatFormFieldModule } from '@angular/material/form-field';
-import { MatInputModule } from '@angular/material/input';
-import { MatSelectModule } from '@angular/material/select';
-import { MatIconModule } from '@angular/material/icon';
-
-interface RefundRequestData {
-  projectId: string;
-  projectName: string;
-  projectAmount: number;
-}
-
-@Component({
-  selector: 'app-refund-request-dialog',
-  standalone: true,
-  imports: [
-    CommonModule,
-    FormsModule,
-    MatButtonModule,
-    MatDialogModule,
-    MatFormFieldModule,
-    MatInputModule,
-    MatSelectModule,
-    MatIconModule
-  ],
-  template: `
-    <h2 mat-dialog-title>申请退款</h2>
-    <mat-dialog-content>
-      <div class="dialog-content">
-        <div class="project-info">
-          <p><strong>项目名称:</strong> {{ data.projectName }}</p>
-          <p><strong>总金额:</strong> ¥{{ data.projectAmount.toLocaleString() }}</p>
-        </div>
-        
-        <div class="form-group">
-          <label for="refund-type">退款类型 *</label>
-          <mat-form-field appearance="outline" class="form-field">
-            <mat-select [(ngModel)]="refundType" id="refund-type" name="refundType" required>
-              <mat-option value="full">全额退款</mat-option>
-              <mat-option value="partial">部分退款</mat-option>
-            </mat-select>
-          </mat-form-field>
-        </div>
-        
-        @if (refundType === 'partial') {
-          <div class="form-group">
-            <label for="refund-amount">退款金额 *</label>
-            <mat-form-field appearance="outline" class="form-field">
-              <input 
-                matInput 
-                [(ngModel)]="refundAmount" 
-                id="refund-amount" 
-                name="refundAmount" 
-                type="number" 
-                min="0" 
-                [max]="data.projectAmount"
-                step="0.01" 
-                placeholder="请输入退款金额"
-                required
-              >
-              <span matTextSuffix>¥</span>
-            </mat-form-field>
-          </div>
-        }
-        
-        <div class="form-group">
-          <label for="refund-reason">退款原因 *</label>
-          <mat-form-field appearance="outline" class="form-field">
-            <textarea 
-              matInput 
-              [(ngModel)]="refundReason" 
-              id="refund-reason" 
-              name="refundReason" 
-              rows="4" 
-              placeholder="请详细描述退款原因..."
-              required
-            ></textarea>
-          </mat-form-field>
-        </div>
-        
-        <div class="form-group">
-          <label for="refund-account">退款账户信息</label>
-          <mat-form-field appearance="outline" class="form-field">
-            <input 
-              matInput 
-              [(ngModel)]="refundAccount" 
-              id="refund-account" 
-              name="refundAccount" 
-              type="text" 
-              placeholder="请提供退款账户信息(如银行卡号/支付宝等)"
-            >
-          </mat-form-field>
-        </div>
-      </div>
-    </mat-dialog-content>
-    <mat-dialog-actions align="end">
-      <button mat-button (click)="onCancel()">取消</button>
-      <button mat-button color="primary" (click)="onSubmit()" [disabled]="!isFormValid()">
-        提交申请
-      </button>
-    </mat-dialog-actions>
-  `,
-  styles: [`
-    .dialog-content {
-      padding: 10px 0;
-    }
-    
-    .project-info {
-      padding: 12px;
-      background-color: #ffebee;
-      border-radius: 8px;
-      margin-bottom: 16px;
-    }
-    
-    .form-group {
-      margin-bottom: 20px;
-    }
-    
-    label {
-      display: block;
-      margin-bottom: 6px;
-      font-weight: 500;
-      color: rgba(0, 0, 0, 0.87);
-    }
-    
-    p {
-      margin: 0;
-      color: rgba(0, 0, 0, 0.6);
-    }
-    
-    .form-field {
-      width: 100%;
-    }
-    
-    .mat-mdc-dialog-actions {
-      padding: 16px 24px;
-    }
-  `]
-}) 
-export class RefundRequestDialog {
-  refundType: string = 'full';
-  refundAmount: number = 0;
-  refundReason: string = '';
-  refundAccount: string = '';
-
-  constructor(
-    public dialogRef: MatDialogRef<RefundRequestDialog>,
-    @Inject(MAT_DIALOG_DATA) public data: RefundRequestData
-  ) {}
-
-  onCancel(): void {
-    this.dialogRef.close();
-  }
-
-  isFormValid(): boolean {
-    if (!this.refundType || !this.refundReason.trim()) {
-      return false;
-    }
-    
-    if (this.refundType === 'partial' && (this.refundAmount <= 0 || this.refundAmount > this.data.projectAmount)) {
-      return false;
-    }
-    
-    return true;
-  }
-
-  onSubmit(): void {
-    if (!this.isFormValid()) return;
-    
-    const formData = {
-      projectId: this.data.projectId,
-      projectName: this.data.projectName,
-      refundType: this.refundType,
-      totalAmount: this.data.projectAmount,
-      refundAmount: this.refundType === 'full' ? this.data.projectAmount : this.refundAmount,
-      refundReason: this.refundReason,
-      refundAccount: this.refundAccount,
-      requestedAt: new Date().toISOString()
-    };
-    
-    this.dialogRef.close({ confirmed: true, data: formData });
-  }
-}

+ 2 - 28
src/app/pages/customer-service/project-list/project-list.html

@@ -31,7 +31,7 @@
             <button [class.active]="viewMode() === 'list'" (click)="toggleView('list')">列表</button>
             <button [class.active]="viewMode() === 'dashboard'" (click)="toggleView('dashboard')">监控大盘</button>
           </div>
-          <button class="add-project-btn" (click)="openCreateProjectModal()">
+          <button class="add-project-btn" (click)="navigateToCreateOrder()">
             <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
               <line x1="12" y1="5" x2="12" y2="19"></line>
               <line x1="5" y1="12" x2="19" y2="12"></line>
@@ -254,32 +254,7 @@
         }
       }
 
-      <!-- 添加项目弹窗 -->
-      @if (createModalVisible()) {
-        <div class="modal-backdrop" (click)="cancelCreateProject()"></div>
-        <div class="modal">
-          <div class="modal-header">
-            <h3>添加项目</h3>
-            <button class="close-btn" (click)="cancelCreateProject()">✖</button>
-          </div>
-          <div class="modal-body">
-            <div class="form-item">
-              <label>客户名称</label>
-              <input type="text" [value]="newCustomerName()" (input)="newCustomerName.set($any($event.target).value)" placeholder="请输入客户名称" />
-            </div>
-            <div class="form-item">
-              <label>核心需求</label>
-              <textarea rows="4" [value]="newRequirement()" (input)="newRequirement.set($any($event.target).value)" placeholder="请输入客户核心需求...">
-              </textarea>
-            </div>
-            <div class="tip">仅需填写以上两项,后续信息在详情页补充</div>
-          </div>
-          <div class="modal-footer">
-            <button class="btn-secondary" (click)="cancelCreateProject()">取消</button>
-            <button class="btn-primary" (click)="submitCreateProject()">提交创建</button>
-          </div>
-        </div>
-      }
+
 
       <!-- 视图:监控大盘模式 -->
       @if (viewMode() === 'dashboard') {
@@ -297,4 +272,3 @@
         </div>
       }
     </div>
-</div>

+ 1 - 1
src/app/pages/customer-service/project-list/project-list.scss

@@ -1196,4 +1196,4 @@ $transition: all 0.3s ease;
       }
     }
   }
-}
+}

+ 59 - 72
src/app/pages/customer-service/project-list/project-list.ts

@@ -1,8 +1,10 @@
-import { Component, OnInit, signal, computed } from '@angular/core';
+import { Component, OnInit, OnDestroy, signal, computed, Inject } from '@angular/core';
 import { CommonModule } from '@angular/common';
 import { FormsModule } from '@angular/forms';
 import { Router, RouterModule } from '@angular/router';
+import { MatDialog, MatDialogModule } from '@angular/material/dialog';
 import { ProjectService } from '../../../services/project.service';
+import { ConsultationOrderDialogComponent } from '../consultation-order/consultation-order-dialog.component';
 import { Project, ProjectStatus, ProjectStage } from '../../../models/project.model';
 
 // 定义项目列表项接口,包含计算后的属性
@@ -16,11 +18,11 @@ interface ProjectListItem extends Project {
 @Component({
   selector: 'app-project-list',
   standalone: true,
-  imports: [CommonModule, FormsModule, RouterModule],
+  imports: [CommonModule, FormsModule, RouterModule, MatDialogModule],
   templateUrl: './project-list.html',
   styleUrls: ['./project-list.scss', '../customer-service-styles.scss']
 })
-export class ProjectList implements OnInit {
+export class ProjectList implements OnInit, OnDestroy {
   // 项目列表数据
   projects = signal<ProjectListItem[]>([]);
   
@@ -38,14 +40,12 @@ export class ProjectList implements OnInit {
     { id: 'done', name: '已完成' }
   ] as const;
 
-  // 创建项目弹窗
-  createModalVisible = signal(false);
-  newCustomerName = signal('');
-  newRequirement = signal('');
-
   // 基础项目集合(服务端返回 + 本地生成),用于二次处理
   private baseProjects: Project[] = [];
 
+  // 消息监听器
+  private messageListener?: (event: MessageEvent) => void;
+
   // 添加toggleSidebar方法
   toggleSidebar(): void {
     // 侧边栏切换逻辑
@@ -101,7 +101,11 @@ export class ProjectList implements OnInit {
     { value: 'name', label: '项目名称' }
   ];
   
-  constructor(private projectService: ProjectService, private router: Router) {}
+  constructor(
+    private projectService: ProjectService, 
+    private router: Router,
+    private dialog: MatDialog
+  ) {}
   
   ngOnInit(): void {
     // 读取上次的视图记忆
@@ -110,8 +114,24 @@ export class ProjectList implements OnInit {
       this.viewMode.set(saved as 'card' | 'list' | 'dashboard');
     }
     this.loadProjects();
+
+    // 添加消息监听器,处理来自iframe的导航请求
+    this.messageListener = (event: MessageEvent) => {
+      // 验证消息来源(可以根据需要添加更严格的验证)
+      if (event.data && event.data.type === 'navigate' && event.data.route) {
+        this.router.navigate([event.data.route]);
+      }
+    };
+    window.addEventListener('message', this.messageListener);
   }
-  
+
+  ngOnDestroy(): void {
+    // 清理消息监听器
+    if (this.messageListener) {
+      window.removeEventListener('message', this.messageListener);
+    }
+  }
+
   // 视图切换
   toggleView(mode: 'card' | 'list' | 'dashboard') {
     if (this.viewMode() !== mode) {
@@ -279,6 +299,7 @@ export class ProjectList implements OnInit {
         highPriorityNeeds: i % 3 === 0 ? ['需快速交付', '重要客户'] : [],
         status: status,
         currentStage: stage,
+        stage: stage,
         createdAt: createdDate,
         deadline: deadlineDate,
         assigneeId: i % 4 === 0 ? '' : `designer${i % 3 + 1}`,
@@ -416,77 +437,43 @@ export class ProjectList implements OnInit {
     return 'req';
   }
 
-  // 详情跳转(附带角色与模块)
+  // 详情跳转到设计师项目详情页面,传递客服角色标识
   navigateToProject(project: ProjectListItem, columnId: 'pending' | 'req' | 'delivery' | 'done') {
-    const tab = columnId === 'pending' ? 'members' : (columnId === 'req' ? 'requirements' : 'overview');
-    this.router.navigate(['/customer-service/project-detail', project.id], {
-      queryParams: { role: 'customer_service', activeTab: tab }
+    this.router.navigate(['/designer/project-detail', project.id], { 
+      queryParams: { role: 'customer-service' } 
     });
   }
 
   // 新增:直接进入沟通管理(消息)标签
   navigateToMessages(project: ProjectListItem) {
-    this.router.navigate(['/customer-service/project-detail', project.id], {
-      queryParams: { role: 'customer_service', activeTab: 'messages' }
-    });
+    this.router.navigate(['/customer-service/messages'], { queryParams: { projectId: project.id } });
   }
-  
-  // 打开/关闭创建项目弹窗
-  openCreateProjectModal() {
-    this.newCustomerName.set('');
-    this.newRequirement.set('');
-    this.createModalVisible.set(true);
-  }
-
-  cancelCreateProject() {
-    this.createModalVisible.set(false);
-  }
-
-  // 提交创建项目(最小必填:客户名称 + 核心需求)
-  submitCreateProject() {
-    const customerName = this.newCustomerName().trim();
-    const requirementText = this.newRequirement().trim();
-    if (!customerName || !requirementText) {
-      alert('请填写客户名称和核心需求');
-      return;
-    }
 
-    const payload = {
-      customerId: 'temp-' + Date.now(),
-      customerName,
-      requirement: requirementText,
-      referenceCases: [],
-      tags: { followUpStatus: '待分配' }
-    };
+  // 导航到创建订单页面
+  navigateToCreateOrder() {
+    // 打开咨询订单弹窗
+    const dialogRef = this.dialog.open(ConsultationOrderDialogComponent, {
+      width: '900px',
+      maxWidth: '95vw',
+      maxHeight: '90vh',
+      panelClass: 'consultation-order-dialog'
+    });
 
-    this.projectService.createProject(payload).subscribe(res => {
-      if (res.success) {
-        // 组装前端项目对象(默认待分配:无assignee)
-        const now = new Date();
-        const deadline = new Date(now.getTime() + 14 * 24 * 60 * 60 * 1000);
-        const newProject: Project = {
-          id: res.projectId,
-          name: `${customerName} 项目`,
-          customerName,
-          customerTags: [],
-          highPriorityNeeds: [],
-          status: '进行中',
-          currentStage: '需求沟通',
-          createdAt: now,
-          deadline: deadline,
-          assigneeId: '',
-          assigneeName: '',
-          skillsRequired: []
-        };
-        this.baseProjects = [newProject, ...this.baseProjects];
-        this.processProjects(this.baseProjects);
-        this.createModalVisible.set(false);
-        // 新建后滚动到“待分配”列顶部
-        setTimeout(() => {
-          const el = document.querySelector('.kanban-column[data-col="pending"]');
-          el?.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'start' });
-        }, 0);
-      }
+    // 监听订单创建成功事件
+    dialogRef.componentInstance.orderCreated.subscribe((orderData: any) => {
+      // 关闭弹窗
+      dialogRef.close();
+      
+      // 跳转到新创建的项目详情页面
+      this.router.navigate([
+        '/designer/project-detail', 
+        orderData.orderId
+      ], {
+        queryParams: { 
+          role: 'customer-service', 
+          activeTab: 'overview'
+        }
+      });
     });
   }
 }

+ 1 - 1
src/app/pages/designer/dashboard/dashboard.ts

@@ -33,7 +33,7 @@ export class Dashboard implements OnInit {
   // 视图管理
   activeDashboard: 'main' | 'skills' | 'personal' = 'main';
   // 新增:工作台视图模式(卡片/列表)
-  viewMode: 'card' | 'list' = 'card';
+  viewMode: 'card' | 'list' = 'list';
   
   tasks: Task[] = [];
   overdueTasks: Task[] = [];

+ 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;
+  }
+}

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

@@ -0,0 +1,14 @@
+<div class="horizontal-tab-nav">
+  <div class="tab-container" #tabContainer>
+    @for (item of navItems; track item.id) {
+      <button 
+        class="tab-item"
+        [class.active]="isActive(item.id)"
+        (click)="onTabClick(item.id)"
+        type="button">
+        <span class="tab-text">{{ item.name }}</span>
+      </button>
+    }
+  </div>
+  <div class="tab-indicator" [style.transform]="'translateX(' + getIndicatorPosition() + 'px)'"></div>
+</div>

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

@@ -0,0 +1,148 @@
+@use '../../../../../shared/styles/_ios-theme.scss' as *;
+
+// 横向滑动条式选项卡导航
+.horizontal-tab-nav {
+  position: relative;
+  display: flex;
+  flex-direction: column;
+  width: 100%;
+  background: $ios-background;
+  border-radius: $ios-radius-lg;
+  overflow: hidden;
+  box-shadow: $ios-shadow-sm;
+}
+
+.tab-container {
+  display: flex;
+  position: relative;
+  background: rgba(0, 122, 255, 0.05);
+  border-radius: $ios-radius-lg;
+  padding: 4px;
+}
+
+.tab-item {
+  flex: 1;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  padding: 12px 16px;
+  border: none;
+  background: transparent;
+  color: $ios-text-secondary;
+  font-size: $ios-font-size-md;
+  font-weight: $ios-font-weight-medium;
+  cursor: pointer;
+  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
+  border-radius: $ios-radius-md;
+  position: relative;
+  z-index: 2;
+  min-width: 120px;
+
+  &:hover {
+    color: $ios-primary;
+    background: rgba(0, 122, 255, 0.08);
+  }
+
+  &.active {
+    color: $ios-primary;
+    font-weight: $ios-font-weight-semibold;
+  }
+
+  .tab-text {
+    white-space: nowrap;
+    overflow: hidden;
+    text-overflow: ellipsis;
+    transition: all 0.3s ease;
+  }
+}
+
+// 滑动指示器
+.tab-indicator {
+  position: absolute;
+  top: 4px;
+  left: 4px;
+  height: calc(100% - 8px);
+  width: 120px;
+  background: $ios-background;
+  border-radius: $ios-radius-md;
+  box-shadow: 0 2px 8px rgba(0, 122, 255, 0.15);
+  transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
+  z-index: 1;
+}
+
+// 响应式设计
+@media (max-width: 768px) {
+  .horizontal-tab-nav {
+    margin: 0 -8px;
+  }
+  
+  .tab-item {
+    padding: 10px 12px;
+    font-size: $ios-font-size-sm;
+    min-width: 100px;
+  }
+  
+  .tab-indicator {
+    width: 100px;
+  }
+}
+
+@media (max-width: 480px) {
+  .tab-item {
+    padding: 8px 10px;
+    font-size: 13px;
+    min-width: 80px;
+  }
+  
+  .tab-indicator {
+    width: 80px;
+  }
+}
+
+// 保持向后兼容的垂直导航样式(如果需要)
+.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%;
+
+  .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;
+    }
+
+    .nav-text {
+      white-space: nowrap;
+      overflow: hidden;
+      text-overflow: ellipsis;
+      flex: 1;
+    }
+  }
+}

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

@@ -0,0 +1,56 @@
+import { Component, Input, Output, EventEmitter, AfterViewInit, ElementRef, ViewChild } 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 implements AfterViewInit {
+  @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'>();
+  @ViewChild('tabContainer', { static: false }) tabContainer!: ElementRef;
+
+  private tabWidth = 120; // 默认选项卡宽度
+
+  ngAfterViewInit(): void {
+    // 动态计算选项卡宽度
+    this.calculateTabWidth();
+  }
+
+  onTabClick(tabId: 'progress' | 'members' | 'files'): void {
+    this.tabChange.emit(tabId);
+  }
+
+  isActive(tabId: 'progress' | 'members' | 'files'): boolean {
+    return this.activeTab === tabId;
+  }
+
+  // 计算滑动指示器的位置
+  getIndicatorPosition(): number {
+    const activeIndex = this.navItems.findIndex(item => item.id === this.activeTab);
+    return activeIndex * this.tabWidth;
+  }
+
+  // 动态计算选项卡宽度
+  private calculateTabWidth(): void {
+    if (this.tabContainer) {
+      const containerWidth = this.tabContainer.nativeElement.offsetWidth;
+      this.tabWidth = (containerWidth - 8) / this.navItems.length; // 减去padding
+    }
+  }
+}

+ 310 - 12
src/app/pages/designer/project-detail/debug-styles.scss

@@ -42,6 +42,99 @@
   margin: 0 !important;
 }
 
+/* 上传区域样式优化 - 确保在并排显示时内容清晰可读 */
+.upload-section {
+  /* 基础样式保持不变 */
+  margin-bottom: 16px !important;
+  
+  /* 针对四个卡片并排时的特殊优化 */
+  .stage-progress-container:has(.vertical-stage-block:nth-child(4):last-child) & {
+    /* 笔记本端和PC端的上传区域优化 */
+    @media (min-width: 1366px) {
+      margin-bottom: 12px !important;
+      
+      .upload-dropzone {
+        min-height: 120px !important;
+        padding: 12px !important;
+        
+        .upload-text {
+          font-size: 12px !important;
+          line-height: 1.4 !important;
+        }
+        
+        .upload-hint {
+          font-size: 11px !important;
+          margin-top: 4px !important;
+        }
+      }
+    }
+    
+    /* 24寸PC端的上传区域优化 */
+    @media (min-width: 1920px) {
+      margin-bottom: 16px !important;
+      
+      .upload-dropzone {
+        min-height: 140px !important;
+        padding: 16px !important;
+        
+        .upload-text {
+          font-size: 13px !important;
+          line-height: 1.4 !important;
+        }
+        
+        .upload-hint {
+          font-size: 12px !important;
+          margin-top: 6px !important;
+        }
+      }
+    }
+    
+    /* 较小屏幕时保持紧凑布局 */
+    @media (max-width: 1365px) {
+      margin-bottom: 10px !important;
+      
+      .upload-dropzone {
+        min-height: 100px !important;
+        padding: 10px !important;
+        
+        .upload-text {
+          font-size: 11px !important;
+          line-height: 1.3 !important;
+        }
+        
+        .upload-hint {
+          font-size: 10px !important;
+          margin-top: 3px !important;
+        }
+      }
+    }
+  }
+}
+
+/* 阶段标题样式优化 */
+.vertical-stage-header {
+  /* 针对四个卡片并排时的标题优化 */
+  .stage-progress-container:has(.vertical-stage-block:nth-child(4):last-child) & {
+    margin-bottom: 12px !important;
+    
+    h4 {
+      font-size: 14px !important;
+      line-height: 1.3 !important;
+      margin: 0 !important;
+      
+      /* 笔记本端和PC端的标题优化 */
+      @media (min-width: 1366px) {
+        font-size: 15px !important;
+      }
+      
+      /* 24寸PC端的标题优化 */
+      @media (min-width: 1920px) {
+        font-size: 16px !important;
+      }
+    }
+  }
+}
+
 /* 强制覆盖主内容布局样式 - 使用最高优先级 */
 .progress-tab-content > .main-content-layout {
   display: flex !important;
@@ -49,34 +142,34 @@
   gap: 20px !important;
   margin-top: 20px !important;
   width: 100% !important;
-  background-color: transparent !important; // 去除调试底色
+  background-color: transparent !important; /* 去除调试底色 */
   padding: 20px !important;
   border-radius: 8px !important;
 }
 
 /* 强制覆盖左侧列样式 - 使用最高优先级 */
 .progress-tab-content > .main-content-layout > .left-column {
-  width: 33.333% !important;
-  min-width: 33.333% !important;
-  max-width: 33.333% !important;
+  width: 28% !important;
+  min-width: 28% !important;
+  max-width: 28% !important;
   display: flex !important;
   flex-direction: column !important;
-  gap: 20px !important;
+  gap: 16px !important;
   background-color: transparent !important; // 去除左侧粉色底色
-  padding: 10px !important;
+  padding: 8px !important;
   border-radius: 6px !important;
 }
 
 /* 强制覆盖右侧列样式 - 使用最高优先级 */
 .progress-tab-content > .main-content-layout > .right-column {
-  width: 66.667% !important;
-  min-width: 66.667% !important;
-  max-width: 66.667% !important;
+  width: 72% !important;
+  min-width: 72% !important;
+  max-width: 72% !important;
   display: flex !important;
   flex-direction: column !important;
-  gap: 20px !important;
+  gap: 16px !important;
   background-color: transparent !important; // 去除右侧调试底色
-  padding: 10px !important;
+  padding: 8px !important;
   border-radius: 6px !important;
 }
 
@@ -85,11 +178,91 @@
 .right-column .card {
   background-color: white !important;
   border-radius: 8px !important;
-  padding: 20px !important;
+  padding: 16px !important;
   box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1) !important;
   border: 1px solid #eaeaea !important;
 }
 
+/* 客户信息卡片内部元素优化 */
+.left-column .project-info-card .info-grid {
+  display: grid !important;
+  grid-template-columns: 1fr 1fr !important;
+  gap: 8px !important;
+  margin-bottom: 12px !important;
+}
+
+.left-column .project-info-card .info-item {
+  display: flex !important;
+  flex-direction: column !important;
+  padding: 8px !important;
+  background: #f8f9fa !important;
+  border-radius: 6px !important;
+  border: 1px solid #e9ecef !important;
+  font-size: 12px !important;
+}
+
+.left-column .project-info-card .info-item label {
+  font-weight: 500 !important;
+  color: #6c757d !important;
+  margin-bottom: 4px !important;
+  font-size: 11px !important;
+}
+
+.left-column .project-info-card .info-item span {
+  color: #212529 !important;
+  font-weight: 600 !important;
+  font-size: 12px !important;
+}
+
+.left-column .project-info-card .tags-container {
+  margin-top: 8px !important;
+}
+
+.left-column .project-info-card .tag-section {
+  margin-bottom: 8px !important;
+}
+
+.left-column .project-info-card .tag-section h3 {
+  font-size: 12px !important;
+  font-weight: 600 !important;
+  color: #495057 !important;
+  margin-bottom: 6px !important;
+}
+
+.left-column .project-info-card .tags .tag {
+  font-size: 10px !important;
+  padding: 4px 8px !important;
+  margin: 2px 4px 2px 0 !important;
+  background-color: rgba(24, 144, 255, 0.08) !important;
+  color: #1890ff !important;
+  border-radius: 4px !important;
+}
+
+.left-column .project-info-card .need-list {
+  margin: 0 !important;
+  padding-left: 12px !important;
+}
+
+.left-column .project-info-card .need-list li {
+  font-size: 11px !important;
+  color: #495057 !important;
+  margin-bottom: 3px !important;
+}
+
+.left-column .project-info-card .requirement-sync-info {
+  margin-top: 8px !important;
+  padding: 8px !important;
+  background: #f1f3f4 !important;
+  border-radius: 6px !important;
+}
+
+.left-column .project-info-card .requirement-sync-info h4 {
+  font-size: 11px !important;
+  font-weight: 600 !important;
+  color: #495057 !important;
+  margin-bottom: 6px !important;
+}
+
 /* 确保响应式布局正常工作 */
 @media (max-width: 1024px) {
   .progress-tab-content > .main-content-layout {
@@ -275,4 +448,129 @@
   line-height: 1.4;
   font-weight: 600;
   box-shadow: 0 4px 10px rgba(255, 77, 79, 0.25);
+}
+
+/* 阶段卡片横向排列(按板块的阶段数量自适应列数) */
+.stage-progress-container {
+  display: grid !important;
+  gap: 12px !important;
+  align-items: stretch !important; // 保证同一行的卡片等高
+  
+  /* 针对交付执行阶段的四个卡片进行特殊处理 */
+  &:has(.vertical-stage-block:nth-child(4):last-child) {
+    /* 当有且仅有4个卡片时,强制并排显示 */
+    grid-template-columns: repeat(4, 1fr) !important;
+    
+    /* 笔记本端适配(1366px及以上) */
+    @media (min-width: 1366px) {
+      grid-template-columns: repeat(4, 1fr) !important;
+      gap: 16px !important;
+    }
+    
+    /* 24寸PC端适配(1920px及以上) */
+    @media (min-width: 1920px) {
+      grid-template-columns: repeat(4, 1fr) !important;
+      gap: 20px !important;
+    }
+    
+    /* 较小屏幕的降级处理(小于1366px) */
+    @media (max-width: 1365px) {
+      grid-template-columns: repeat(2, 1fr) !important;
+      gap: 12px !important;
+    }
+  }
+  
+  /* 其他情况保持原有的自适应布局 */
+  &:not(:has(.vertical-stage-block:nth-child(4):last-child)) {
+    grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)) !important;
+  }
+}
+
+.vertical-stage-block {
+  display: flex !important;
+  flex-direction: column !important;
+  height: 100% !important;
+  padding: 12px !important;
+  background: white !important;
+  border-radius: 8px !important;
+  border: 1px solid #e9ecef !important;
+  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.08) !important;
+  
+  /* 确保卡片内容在并排显示时保持可读性 */
+  min-width: 0 !important; /* 防止内容溢出 */
+  
+  /* 针对四个卡片并排时的特殊样式 */
+  .stage-progress-container:has(.vertical-stage-block:nth-child(4):last-child) & {
+    /* 笔记本端和PC端的内容优化 */
+    @media (min-width: 1366px) {
+      padding: 16px !important;
+      min-height: 300px !important; /* 确保卡片有足够高度 */
+    }
+    
+    /* 24寸PC端的内容优化 */
+    @media (min-width: 1920px) {
+      padding: 20px !important;
+      min-height: 320px !important;
+    }
+    
+    /* 较小屏幕时保持紧凑布局 */
+    @media (max-width: 1365px) {
+      padding: 12px !important;
+      min-height: 280px !important;
+    }
+  }
+}
+
+.vertical-stage-header {
+  display: flex !important;
+  align-items: center !important;
+  gap: 8px !important;
+  margin-bottom: 8px !important;
+  padding-bottom: 6px !important;
+  border-bottom: 1px solid #f1f3f4 !important;
+}
+
+.vertical-stage-header h3 {
+  font-size: 13px !important;
+  font-weight: 600 !important;
+  color: #495057 !important;
+  margin: 0 !important;
+}
+
+.vertical-stage-header .dot {
+  width: 8px !important;
+  height: 8px !important;
+  border-radius: 50% !important;
+  background: #dee2e6 !important;
+  flex-shrink: 0 !important;
+}
+
+.vertical-stage-header .dot.completed {
+  background: #28a745 !important;
+}
+
+.vertical-stage-header .dot.active {
+  background: #007bff !important;
+}
+
+.vertical-stage-body {
+  display: flex !important;
+  flex-direction: column !important;
+  gap: 8px !important;
+  flex: 1 1 auto !important;
+}
+
+/* 活动阶段卡片优化 */
+.vertical-stage-block.active {
+  background: #fff8f0 !important;
+  border-color: #ffc069 !important;
+  box-shadow: 0 2px 8px rgba(255, 192, 105, 0.2) !important;
+}
+
+.vertical-stage-block.active .vertical-stage-header h3 {
+  color: #d46b08 !important;
+}
+
+.vertical-stage-block.active .dot {
+  background: #fa8c16 !important;
 }

+ 841 - 408
src/app/pages/designer/project-detail/project-detail.html

@@ -1,48 +1,137 @@
-<!-- 只展示修改处,未变更部分用占位注释表示 -->
+<!-- 只展示修改处,未变更部分用占位注释表示 -->
 <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">
+        <!-- 导航条 - 移动到顶部与导出按钮水平对齐 -->
+        <div class="top-nav-container">
+          <app-vertical-nav 
+            [activeTab]="activeTab" 
+            (tabChange)="switchTab($event)"
+            class="top-nav">
+          </app-vertical-nav>
+        </div>
+        
+        <!-- 导出阶段报告 -->
+        <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>
+
+<!-- 四大板块按钮组 - 位于项目详情标题区域正下方 -->
+<div class="sections-toolbar-header">
+  @for (sec of sections; track sec.key) {
+    <button class="section-btn"
+            [class.completed]="getSectionStatus(sec.key) === 'completed'"
+            [class.active]="getSectionStatus(sec.key) === 'active'"
+            [class.pending]="getSectionStatus(sec.key) === 'pending'"
+            (click)="toggleSection(sec.key)">
+      <span class="section-label">{{ sec.label }}</span>
+    </button>
+  }
+</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 +146,8 @@
   <!-- 顶部导航标签页 -->
   <!-- 原有代码保留 -->
 
+  <!-- 水平导航栏 - 已移动到顶部,此处删除 -->
+
   <div class="tab-content">
     <!-- 项目进度标签页 -->
     @if (isActiveTab('progress')) {
@@ -68,8 +159,19 @@
               <h2>客户信息</h2>
               @if (project) {
                 <div class="info-grid">
-                  <div class="info-item key-info"><label>客户姓名</label><span>{{ project.customerName }}</span></div>
-                  <div class="info-item key-info"><label>项目负责人</label><span>{{ project.assigneeName }}</span></div>
+                  <!-- 显示订单创建时填写的客户信息,如果有的话 -->
+                  @if (orderCreationData?.customerInfo) {
+                    <div class="info-item key-info"><label>客户姓名</label><span>{{ orderCreationData.customerInfo.name }}</span></div>
+                    <div class="info-item key-info"><label>手机号码</label><span>{{ orderCreationData.customerInfo.phone }}</span></div>
+                    @if (orderCreationData.customerInfo.wechat) {
+                      <div class="info-item"><label>微信号</label><span>{{ orderCreationData.customerInfo.wechat }}</span></div>
+                    }
+                    <div class="info-item"><label>客户类型</label><span>{{ orderCreationData.customerInfo.customerType }}</span></div>
+                  } @else {
+                    <!-- 默认显示项目信息 -->
+                    <div class="info-item key-info"><label>客户姓名</label><span>{{ project.customerName }}</span></div>
+                    <div class="info-item key-info"><label>项目负责人</label><span>{{ project.assigneeName }}</span></div>
+                  }
                   <div class="info-item"><label>项目创建</label><span>{{ formatDate(project.createdAt) }}</span></div>
                   <div class="info-item"><label>截止日期</label><span>{{ formatDate(project.deadline) }}</span></div>
                 </div>
@@ -91,6 +193,115 @@
                       }
                       @if (project.highPriorityNeeds.length === 0) { <li class="desc">无</li> }
                     </ul>
+                    
+                    <!-- 新增:需求关键信息同步区域 -->
+                    <div class="requirement-sync-info">
+                      <h4>项目需求信息</h4>
+                      
+                      <!-- 显示订单创建时填写的需求信息 -->
+                      @if (orderCreationData?.requirementInfo) {
+                        <div class="order-requirement-info">
+                          <h5>订单创建阶段需求</h5>
+                          <div class="key-info-grid">
+                            @if (orderCreationData.requirementInfo.decorationType) {
+                              <div class="info-item">
+                                <span class="info-label">装修类型</span>
+                                <span class="info-value">{{ orderCreationData.requirementInfo.decorationType }}</span>
+                              </div>
+                            }
+                            @if (orderCreationData.requirementInfo.downPayment) {
+                              <div class="info-item">
+                                <span class="info-label">首付款</span>
+                                <span class="info-value">¥{{ orderCreationData.requirementInfo.downPayment }}</span>
+                              </div>
+                            }
+                            @if (orderCreationData.requirementInfo.firstDraftDate) {
+                              <div class="info-item">
+                                <span class="info-label">首稿时间</span>
+                                <span class="info-value">{{ orderCreationData.requirementInfo.firstDraftDate }}</span>
+                              </div>
+                            }
+                            @if (orderCreationData.requirementInfo.style) {
+                              <div class="info-item">
+                                <span class="info-label">装修风格</span>
+                                <span class="info-value">{{ orderCreationData.requirementInfo.style }}</span>
+                              </div>
+                            }
+                            @if (orderCreationData.requirementInfo.spaceRequirements) {
+                              <div class="info-item">
+                                <span class="info-label">涉及空间</span>
+                                <span class="info-value">{{ orderCreationData.requirementInfo.spaceRequirements }}</span>
+                              </div>
+                            }
+                          </div>
+                          
+                          <!-- 显示偏好标签 -->
+                          @if (orderCreationData.preferenceTags && orderCreationData.preferenceTags.length > 0) {
+                            <div class="preference-tags">
+                              <span class="info-label">偏好标签:</span>
+                              <div class="tags">
+                                @for (tag of orderCreationData.preferenceTags; track tag) {
+                                  <span class="tag">{{ tag }}</span>
+                                }
+                              </div>
+                            </div>
+                          }
+                        </div>
+                      }
+                      
+                      <!-- 确认需求阶段的关键信息 -->
+                      <div class="confirmed-requirement-info">
+                        <h5>确认需求关键信息</h5>
+                        <div class="key-info-grid">
+                          @if (requirementKeyInfo.colorAtmosphere.description) {
+                            <div class="info-item">
+                              <span class="info-label">色彩氛围</span>
+                              <span class="info-value">{{ requirementKeyInfo.colorAtmosphere.description }}</span>
+                              <div class="color-preview" [style.background-color]="requirementKeyInfo.colorAtmosphere.mainColor"></div>
+                            </div>
+                          }
+                          
+                          @if (requirementKeyInfo.spaceStructure.aspectRatio > 0) {
+                            <div class="info-item">
+                              <span class="info-label">空间结构</span>
+                              <span class="info-value">比例 {{ requirementKeyInfo.spaceStructure.aspectRatio.toFixed(1) }}</span>
+                            </div>
+                          }
+                          
+                          @if (requirementKeyInfo.materialWeights.woodRatio > 0 || requirementKeyInfo.materialWeights.fabricRatio > 0) {
+                            <div class="info-item">
+                              <span class="info-label">材质权重</span>
+                              <div class="material-weights">
+                                @if (requirementKeyInfo.materialWeights.woodRatio > 0) {
+                                  <span class="weight-item">木质 {{ requirementKeyInfo.materialWeights.woodRatio }}%</span>
+                                }
+                                @if (requirementKeyInfo.materialWeights.fabricRatio > 0) {
+                                  <span class="weight-item">布艺 {{ requirementKeyInfo.materialWeights.fabricRatio }}%</span>
+                                }
+                                @if (requirementKeyInfo.materialWeights.metalRatio > 0) {
+                                  <span class="weight-item">金属 {{ requirementKeyInfo.materialWeights.metalRatio }}%</span>
+                                }
+                              </div>
+                            </div>
+                          }
+                          
+                          @if (requirementKeyInfo.presetAtmosphere.name) {
+                            <div class="info-item">
+                              <span class="info-label">预设氛围</span>
+                              <span class="info-value">{{ requirementKeyInfo.presetAtmosphere.name }}</span>
+                              <span class="color-temp">{{ requirementKeyInfo.presetAtmosphere.colorTemp }}</span>
+                            </div>
+                          }
+                        </div>
+                        
+                        @if (getRequirementSummary().length === 0 && !orderCreationData?.requirementInfo) {
+                          <div class="sync-placeholder">
+                            <span class="placeholder-text">暂无同步的需求信息</span>
+                            <button class="sync-btn" (click)="syncRequirementKeyInfo({})">手动同步</button>
+                          </div>
+                        }
+                      </div>
+                    </div>
                   </div>
                 </div>
               } @else {
@@ -107,428 +318,650 @@
             <div class="process-card card">
               <h2>制作流程进度</h2>
 
-              <!-- 新增:四大板块矩形按钮 -->
-              <div class="sections-toolbar">
-                @for (sec of sections; track sec.key) {
-                  <button class="section-btn"
-                          [class.completed]="getSectionStatus(sec.key) === 'completed'"
-                          [class.active]="getSectionStatus(sec.key) === 'active'"
-                          [class.pending]="getSectionStatus(sec.key) === 'pending'"
-                          (click)="toggleSection(sec.key)">
-                    <span class="section-label">{{ sec.label }}</span>
-                  </button>
-                }
-              </div>
-
               <!-- 串式流程: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>
+                @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-consultation-order-panel
+                          [roleContext]="roleContext"
+                          (formSubmit)="onConsultationOrderSubmit($event)"
+                        ></app-consultation-order-panel>
+                      } @else if (stage === '需求沟通') {
+                        <app-requirements-confirm-card 
+                          (requirementConfirmed)="syncRequirementKeyInfo($event)"
+                          (progressUpdated)="syncRequirementKeyInfo($event)"
+                          (stageCompleted)="onRequirementsStageCompleted($event)">
+                        </app-requirements-confirm-card>
+                      } @else if (stage === '方案确认') {
+                        <!-- 方案确认阶段 - 需求信息展示 -->
+                        <div class="proposal-confirm-section">
+                          <div class="section-header">
+                            <h4>方案确认</h4>
+                            <div class="progress-indicator">
+                              <span class="progress-text">{{ getRequiredStagesProgress() }}% 完成</span>
+                            </div>
+                          </div>
+                          
+                          <!-- 需求信息展示区域 -->
+                          @if (areRequiredStagesCompleted()) {
+                            <div class="requirement-info-display">
+                              <div class="info-header">
+                                <h5>确认的需求信息</h5>
+                                <span class="info-subtitle">来自需求沟通阶段</span>
                               </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 class="info-grid">
+                                <!-- 色调和材质并排布局 -->
+                                <div class="color-material-row">
+                                  <!-- 色调 -->
+                                  @if (requirementKeyInfo.colorAtmosphere.description) {
+                                    <div class="info-item color-item">
+                                      <div class="info-label">
+                                        <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>
+                                          <path d="M12 6v6l4 2"></path>
+                                        </svg>
+                                        色调
+                                      </div>
+                                      <div class="info-content">
+                                        <span class="info-value">{{ requirementKeyInfo.colorAtmosphere.description }}</span>
+                                        @if (requirementKeyInfo.colorAtmosphere.mainColor) {
+                                          <div class="color-preview" [style.background-color]="requirementKeyInfo.colorAtmosphere.mainColor"></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> }
+                                  
+                                  <!-- 材质 -->
+                                  @if (requirementKeyInfo.colorAtmosphere.materials && requirementKeyInfo.colorAtmosphere.materials.length > 0) {
+                                    <div class="info-item material-item">
+                                      <div class="info-label">
+                                        <svg width="16" height="16" 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>
+                                          <rect x="7" y="7" width="10" height="10" rx="1" ry="1"></rect>
+                                        </svg>
+                                        材质
+                                      </div>
+                                      <div class="info-content">
+                                        <div class="material-list">
+                                          @for (material of requirementKeyInfo.colorAtmosphere.materials; track material) {
+                                            <span class="material-tag">{{ material }}</span>
+                                          }
+                                        </div>
+                                      </div>
                                     </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 class="structure-weight-row">
+                                  <!-- 空间结构 -->
+                                  @if (requirementKeyInfo.spaceStructure.aspectRatio > 0) {
+                                    <div class="info-item structure-item">
+                                      <div class="info-label">
+                                        <svg width="16" height="16" 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>
+                                          <line x1="9" y1="9" x2="15" y2="15"></line>
+                                        </svg>
+                                        空间结构
                                       </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 class="info-content">
+                                        <div class="structure-details">
+                                          <span class="structure-item">比例: {{ requirementKeyInfo.spaceStructure.aspectRatio.toFixed(1) }}</span>
+                                          @if (requirementKeyInfo.spaceStructure.ceilingHeight > 0) {
+                                            <span class="structure-item">层高: {{ requirementKeyInfo.spaceStructure.ceilingHeight }}m</span>
+                                          }
+                                          @if (requirementKeyInfo.spaceStructure.flowWidth > 0) {
+                                            <span class="structure-item">流线宽度: {{ requirementKeyInfo.spaceStructure.flowWidth }}m</span>
+                                          }
+                                        </div>
                                       </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 (requirementKeyInfo.materialWeights.woodRatio > 0 || requirementKeyInfo.materialWeights.fabricRatio > 0 || requirementKeyInfo.materialWeights.metalRatio > 0) {
+                                    <div class="info-item weight-item">
+                                      <div class="info-label">
+                                        <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
+                                          <path d="M3 3v18h18"></path>
+                                          <path d="M7 16l4-4 4 4 6-6"></path>
+                                        </svg>
+                                        材质权重
                                       </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 class="info-content">
+                                        <div class="weight-list">
+                                          @if (requirementKeyInfo.materialWeights.woodRatio > 0) {
+                                            <div class="weight-item">
+                                              <span class="weight-label">木质</span>
+                                              <span class="weight-value">{{ requirementKeyInfo.materialWeights.woodRatio }}%</span>
+                                            </div>
+                                          }
+                                          @if (requirementKeyInfo.materialWeights.fabricRatio > 0) {
+                                            <div class="weight-item">
+                                              <span class="weight-label">布艺</span>
+                                              <span class="weight-value">{{ requirementKeyInfo.materialWeights.fabricRatio }}%</span>
+                                            </div>
+                                          }
+                                          @if (requirementKeyInfo.materialWeights.metalRatio > 0) {
+                                            <div class="weight-item">
+                                              <span class="weight-label">金属</span>
+                                              <span class="weight-value">{{ requirementKeyInfo.materialWeights.metalRatio }}%</span>
                                             </div>
                                           }
                                         </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>
+                                  }
+                                </div>
+                                
+                                <!-- 预设氛围和小图时间并排布局 -->
+                                <div class="atmosphere-time-row">
+                                  <!-- 预设氛围 -->
+                                  @if (requirementKeyInfo.presetAtmosphere.name) {
+                                    <div class="info-item atmosphere-item">
+                                      <div class="info-label">
+                                        <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
+                                          <path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"></path>
+                                        </svg>
+                                        预设氛围
                                       </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 class="info-content">
+                                        <div class="atmosphere-info">
+                                          <span class="atmosphere-name">{{ requirementKeyInfo.presetAtmosphere.name }}</span>
+                                          @if (requirementKeyInfo.presetAtmosphere.colorTemp) {
+                                            <span class="color-temp">{{ requirementKeyInfo.presetAtmosphere.colorTemp }}</span>
+                                          }
+                                        </div>
                                       </div>
-                                    }
-                                    <div class="upload-section">
-                                      <div class="upload-header">
-                                        <h4>上传渲染大图</h4>
-                                        <span class="hint">需满足4K标准(最大边≥4000px)</span>
+                                    </div>
+                                  }
+                                  
+                                  <!-- 小图时间 -->
+                                  @if (getEstimatedSmallImageTime()) {
+                                    <div class="info-item time-item">
+                                      <div class="info-label">
+                                        <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>
+                                        小图时间
                                       </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 class="info-content">
+                                        <span class="time-estimate">{{ getEstimatedSmallImageTime() }}</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>
-                                  <!-- END: reuse 渲染阶段内容 -->
-                                }
+                                  }
+                                </div>
+                              </div>
+                            </div>
+                            
+                            <!-- 确认方案按钮 -->
+            <div class="proposal-confirm-actions">
+              <button class="confirm-proposal-btn" 
+                      (click)="confirmProposal()"
+                      style="
+                        appearance: none !important;
+                        border: none !important;
+                        background: linear-gradient(135deg, #007aff 0%, #0066ff 50%, #0051d5 100%) !important;
+                        color: white !important;
+                        padding: 16px 40px !important;
+                        border-radius: 16px !important;
+                        font-size: 16px !important;
+                        font-weight: 600 !important;
+                        cursor: pointer !important;
+                        transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1) !important;
+                        display: flex !important;
+                        align-items: center !important;
+                        gap: 10px !important;
+                        box-shadow: 0 8px 24px rgba(0, 122, 255, 0.25), 0 4px 12px rgba(0, 122, 255, 0.15), inset 0 1px 0 rgba(255, 255, 255, 0.2) !important;
+                        min-width: 180px !important;
+                        justify-content: center !important;
+                        position: relative !important;
+                        overflow: hidden !important;
+                        text-transform: none !important;
+                        letter-spacing: normal !important;
+                        line-height: normal !important;
+                      ">
+                <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" style="color: white !important;">
+                  <polyline points="20,6 9,17 4,12"></polyline>
+                </svg>
+                确认方案
+              </button>
+            </div>
+                          } @else {
+                            <div class="requirement-pending">
+                              <div class="pending-icon">
+                                <svg width="48" height="48" 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>
+                              </div>
+                              <h5>等待需求确认完成</h5>
+                              <p>请先完成"流程进度 > 确认需求 > 需求沟通"四个流程,确认需求信息后方可查看。</p>
+                              <div class="progress-status">
+                                <span>当前进度: {{ getRequiredStagesProgress() }}%</span>
                               </div>
                             </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>
-                              <div class="vertical-stage-body">
-                                <!-- 复用原模板各阶段的具体内容(示例:尾款结算等) -->
-                                @if (stage === '尾款结算') {
-                                  @if (settlements.length > 0) {
-                                    <div class="settlement-table">
-                                      <table>
-                                        <thead>
-                                          <tr>
-                                            <th>阶段</th>
-                                            <th>比例</th>
-                                            <th>金额</th>
-                                            <th>状态</th>
-                                            <th>时间</th>
-                                          </tr>
-                                        </thead>
-                                        <tbody>
-                                          @for (st of settlements; track st.id) {
-                                            <tr>
-                                              <td>{{ st.stage }}</td>
-                                              <td>{{ st.percentage }}%</td>
-                                              <td>¥{{ st.amount | number:'1.0-0' }}</td>
-                                              <td>{{ st.status }}</td>
-                                              <td>{{ (st.settledAt || st.createdAt) | date:'MM-dd HH:mm' }}</td>
-                                            </tr>
-                                          }
-                                        </tbody>
-                                      </table>
+                      } @else if (stage === '建模') {
+                        <div class="upload-section">
+                          <div class="upload-header">
+                            <h4>上传白模图片</h4>
+                            <span class="hint">支持:JPG/PNG,不强制4K</span>
+                          </div>
+                          @if (canEditSection('delivery')) {
+                            <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>
                                   }
-                                }
-                                @if (stage === '客户评价') {
-                                  <div class="empty">此处可扩展客户评价表单/列表</div>
-                                }
-                                @if (stage === '投诉处理') {
-                                  <div class="empty">此处可扩展投诉处理流程/记录</div>
-                                }
-                                @if (stage === '订单创建') {
-                                  <div>
-                                    <div style="display:flex; gap:12px; align-items:center; margin-bottom:12px; flex-wrap: wrap;">
-                                      <div style="display:flex; gap:8px; align-items:center;">
-                                        <button class="secondary-btn" [class.active]="orderCreationMethod === 'miniprogram'" (click)="orderCreationMethod = 'miniprogram'">小程序同步</button>
-                                        <button class="secondary-btn" [class.active]="orderCreationMethod === 'manual'" (click)="orderCreationMethod = 'manual'">手动录入</button>
+                                  <div class="add-more-btn" (click)="triggerFileInput('whiteModel')">
+                                    <div class="add-icon">+</div>
+                                    <div class="add-text">添加更多</div>
+                                  </div>
+                                </div>
+                              }
+                              <input type="file" 
+                                     id="whiteModelFileInput"
+                                     accept="{{allowedImageTypes}}" 
+                                     multiple 
+                                     (change)="onWhiteModelSelected($event)" 
+                                     style="display: none;" />
+                            </div>
+                          }
+                          <div class="upload-actions">
+                            @if (canEditSection('delivery')) {
+                              <button class="primary-btn" [disabled]="whiteModelImages.length===0" (click)="confirmWhiteModelUpload()">确认上传</button>
+                            }
+                            @if (isTeamLeaderView()) { <button class="secondary-btn" (click)="syncUploadedImages('white')">同步图片信息</button> }
+                            @if (!canEditSection('delivery')) { <span class="desc">只读</span> }
+                          </div>
+                        </div>
+                        <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]="!canEditSection('delivery')">
+                                  <span class="checklist-text">{{ item.name }}</span>
+                                </label>
+                                <span class="check-status">{{ item.isPassed ? '已通过' : '待处理' }}</span>
+                              </div>
+                            }
+                          </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 (canEditSection('delivery')) {
+                              <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>
+                                        </div>
                                       </div>
-                                      <span class="hint">下单时间:{{ orderTime }}</span>
+                                    }
+                                    <div class="add-more-btn" (click)="triggerFileInput('softDecor')">
+                                      <div class="add-icon">+</div>
+                                      <div class="add-text">添加更多</div>
                                     </div>
-
-                                    @if (orderCreationMethod === 'miniprogram') {
-                                      <div style="display:flex; gap:12px; align-items:center; margin-bottom:12px;">
-                                        <button class="primary-btn" [disabled]="isSyncing" (click)="syncMiniprogramCustomerInfo()">{{ isSyncing ? '同步中...' : '从小程序同步客户信息' }}</button>
-                                        <span class="hint">点击同步后将自动填充客户姓名、手机号、微信等信息</span>
+                                  </div>
+                                }
+                                <input type="file" 
+                                       id="softDecorFileInput"
+                                       accept="{{allowedImageTypes}}" 
+                                       multiple 
+                                       (change)="onSoftDecorSmallPicsSelected($event)" 
+                                       style="display: none;" />
+                              </div>
+                            }
+                            <div class="upload-actions">
+                              @if (canEditSection('delivery')) {
+                                <button class="primary-btn" [disabled]="softDecorImages.length===0" (click)="confirmSoftDecorUpload()">确认上传</button>
+                              }
+                              @if (isTeamLeaderView()) { <button class="secondary-btn" (click)="syncUploadedImages('soft')">同步图片信息</button> }
+                              @if (!canEditSection('delivery')) { <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 (canEditSection('delivery')) {
+                              <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 style="border-top:1px solid #e5e7eb; padding-top:12px; margin-top:4px;"></div>
-
-                                    <div style="margin-bottom:12px; display:flex; gap:8px; align-items:center; flex-wrap: wrap;">
-                                      <input type="text" placeholder="搜索客户姓名或手机号" [(ngModel)]="customerSearchKeyword" (ngModelChange)="searchCustomer()" style="flex:1 1 260px; padding:8px 10px; border:1px solid #d1d5db; border-radius:6px;" />
-                                      <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>
-                                          <button class="link danger" (click)="clearSelectedCustomer()">清除</button>
-                                        </div>
-                                      }
+                                    <div class="add-more-btn" (click)="triggerFileInput('render')">
+                                      <div class="add-icon">+</div>
+                                      <div class="add-text">添加更多</div>
                                     </div>
-
-                                    @if (customerSearchResults.length > 0) {
-                                      <div style="border:1px solid #e5e7eb; border-radius:8px; padding:8px; margin-bottom:12px; max-height:200px; overflow:auto;">
-                                        @for (c of customerSearchResults; track c.id) {
-                                          <div style="display:flex; justify-content:space-between; align-items:center; padding:6px 4px; border-bottom:1px dashed #f3f4f6;">
-                                            <div style="display:flex; gap:8px; align-items:center;">
-                                              <span style="font-weight:500;">{{ c.name }}</span>
-                                              <span style="color:#6b7280;">{{ c.phone }}</span>
-                                              @if (c.wechat) { <span style="color:#9ca3af;">wx: {{ c.wechat }}</span> }
-                                            </div>
-                                            <button class="link" (click)="selectCustomer(c)">选择</button>
+                                  </div>
+                                }
+                                <input type="file" 
+                                       id="renderFileInput"
+                                       accept="{{allowedImageTypes}}" 
+                                       multiple 
+                                       (change)="onRenderLargePicsSelected($event)" 
+                                       style="display: none;" />
+                              </div>
+                            }
+                            <div class="upload-actions">
+                              @if (canEditSection('delivery')) {
+                                <button class="primary-btn" [disabled]="renderLargeImages.length===0" (click)="confirmRenderUpload()">确认上传</button>
+                              }
+                              @if (isTeamLeaderView()) { <button class="secondary-btn" (click)="syncUploadedImages('render')">同步图片信息</button> }
+                              @if (!canEditSection('delivery')) { <span class="desc">只读</span> }
+                            </div>
+                          </div>
+                        </div>
+                      } @else if (stage === '后期') {
+                        <div class="post-process-section">
+                          <div class="upload-section">
+                            <div class="upload-header">
+                              <h4>上传后期处理图片</h4>
+                              <span class="hint">包含调色、修图、特效等后期处理结果</span>
+                            </div>
+                            @if (canEditSection('delivery')) {
+                              <div class="upload-dropzone" 
+                                   (click)="postProcessImages.length === 0 ? triggerFileInput('postProcess') : null"
+                                   (dragover)="postProcessImages.length === 0 ? onDragOver($event) : null"
+                                   (dragleave)="postProcessImages.length === 0 ? onDragLeave($event) : null"
+                                   (drop)="postProcessImages.length === 0 ? onFileDrop($event, 'postProcess') : null"
+                                   [class.drag-over]="isDragOver && postProcessImages.length === 0"
+                                   [class.has-images]="postProcessImages.length > 0">
+                                @if (postProcessImages.length === 0) {
+                                  <div class="upload-icon"></div>
+                                  <div class="upload-text">点击此处或拖拽文件到此处上传</div>
+                                  <div class="upload-hint">支持 JPG、PNG 格式,展示后期处理效果</div>
+                                } @else {
+                                  <div class="uploaded-images-grid">
+                                    @for (img of postProcessImages; 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(); removePostProcessImage(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>
                                     }
-
-                                    <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('postProcess')">
+                                      <div class="add-icon">+</div>
+                                      <div class="add-text">添加更多</div>
+                                    </div>
                                   </div>
                                 }
-                                @if (stage === '需求沟通' || stage === '方案确认') {
-                                  <div class="empty">该阶段的详细表单与内容保持与现有功能一致,后续可按需接入</div>
-                                }
+                                <input type="file" 
+                                       id="postProcessFileInput"
+                                       accept="{{allowedImageTypes}}" 
+                                       multiple 
+                                       (change)="onPostProcessPicsSelected($event)" 
+                                       style="display: none;" />
                               </div>
+                            }
+                            <div class="upload-actions">
+                              @if (canEditSection('delivery')) {
+                                <button class="primary-btn" [disabled]="postProcessImages.length===0" (click)="confirmPostProcessUpload()">确认上传</button>
+                              }
+                              @if (isTeamLeaderView()) { <button class="secondary-btn" (click)="syncUploadedImages('postProcess')">同步图片信息</button> }
+                              @if (!canEditSection('delivery')) { <span class="desc">只读</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>
-}

+ 2181 - 1370
src/app/pages/designer/project-detail/project-detail.scss

@@ -1,1446 +1,2257 @@
-/* 项目详情页面样式文件 - 简化版本,直接针对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: 2%; // 使用百分比单位
   background-color: #f5f7fa;
   color: #333;
   min-height: 100vh;
+  box-sizing: border-box; // 确保内边距计算正确
+  overflow-x: auto; // 允许水平滚动以防内容过宽
 }
 
-/* 项目标题栏 */
-.project-header {
+/* 四大板块按钮组 - 位于项目标题区域正下方 */
+.sections-toolbar-header {
   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; /* 右侧列背景色 */
+  gap: 1%; // 使用百分比间距
+  width: 100%;
+  margin: 1.5% 0 2% 0; // 使用百分比边距
+  padding: 0 2%; /* 与项目详情容器保持一致的左右边距 */
+  
+  .section-btn {
+    flex: 1;
+    appearance: none;
+    border: 1px solid $ios-border;
+    background: $ios-background;
+    color: $ios-text-primary;
+    padding: 0.9rem 1.2rem; // 使用rem单位
+    border-radius: $ios-radius-md;
+    font-size: 0.9rem; // 使用rem单位
+    font-weight: 500;
+    cursor: pointer;
+    transition: all 0.2s ease;
+    text-align: center;
+    min-height: 3rem; // 使用rem单位
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    
+    .section-label {
+      white-space: nowrap;
+      overflow: hidden;
+      text-overflow: ellipsis;
+    }
+    
+    &:hover {
+      transform: translateY(-0.08rem); // 使用rem单位
+      box-shadow: 0 0.3rem 0.9rem rgba(0, 0, 0, 0.1);
+    }
+    
+    &.completed {
+      background: linear-gradient(135deg, #e6f7e6 0%, #d4edda 100%);
+      color: $ios-success;
+      border-color: $ios-success;
+      box-shadow: 0 0.15rem 0.6rem rgba(40, 167, 69, 0.15);
+    }
+    
+    &.active {
+      background: linear-gradient(135deg, #e8f0fe 0%, #cce7ff 100%);
+      color: $ios-primary;
+      border-color: $ios-primary;
+      box-shadow: 0 0.15rem 0.6rem rgba(0, 122, 255, 0.2);
+    }
+    
+    &.pending {
+      background: $ios-background;
+      color: $ios-text-secondary;
+      border-color: rgba(0, 0, 0, 0.1);
+    }
+  }
 }
 
-/* 确保响应式布局正常工作 */
-@media (max-width: 1024px) {
-  .progress-tab-content > .main-content-layout {
-    flex-direction: column !important;
+// 图片预览模态框样式
+.image-preview-modal {
+  position: fixed;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  z-index: 1000;
+  display: flex;
+  align-items: center;
+  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;
+            }
+          }
+        }
+      }
+    }
+
+    // 需求关键信息同步区域样式
+    .requirement-sync-info {
+      margin-top: 16px;
+      padding: 16px;
+      background: rgba(0, 122, 255, 0.02);
+      border: 1px solid rgba(0, 122, 255, 0.1);
+      border-radius: 8px;
+
+      h4 {
+        font-size: 14px;
+        font-weight: 600;
+        color: #1d1d1f;
+        margin: 0 0 16px 0;
+        display: flex;
+        align-items: center;
+        gap: 6px;
+
+        &::before {
+          content: '';
+          width: 3px;
+          height: 14px;
+          background: #007aff;
+          border-radius: 2px;
+        }
+      }
+
+      h5 {
+        font-size: 13px;
+        font-weight: 600;
+        color: #1d1d1f;
+        margin: 0 0 12px 0;
+        padding-bottom: 6px;
+        border-bottom: 1px solid rgba(0, 0, 0, 0.06);
+      }
+
+      .order-requirement-info {
+        margin-bottom: 20px;
+        padding: 12px;
+        background: rgba(52, 199, 89, 0.02);
+        border: 1px solid rgba(52, 199, 89, 0.1);
+        border-radius: 6px;
+
+        .preference-tags {
+          margin-top: 12px;
+          padding-top: 12px;
+          border-top: 1px solid rgba(0, 0, 0, 0.06);
+
+          .info-label {
+            font-weight: 500;
+            color: #666;
+            margin-bottom: 6px;
+            display: block;
+          }
+
+          .tags {
+            display: flex;
+            flex-wrap: wrap;
+            gap: 6px;
+
+            .tag {
+              font-size: 11px;
+              background: rgba(52, 199, 89, 0.1);
+              color: #34c759;
+              padding: 4px 8px;
+              border-radius: 12px;
+              font-weight: 500;
+              border: 1px solid rgba(52, 199, 89, 0.2);
+            }
+          }
+        }
+      }
+
+      .confirmed-requirement-info {
+        padding: 12px;
+        background: rgba(255, 149, 0, 0.02);
+        border: 1px solid rgba(255, 149, 0, 0.1);
+        border-radius: 6px;
+      }
+
+      .key-info-grid {
+        display: grid;
+        gap: 12px;
+
+        .info-item {
+          display: flex;
+          align-items: center;
+          gap: 8px;
+          padding: 8px 12px;
+          background: white;
+          border-radius: 6px;
+          border: 1px solid rgba(0, 0, 0, 0.06);
+          font-size: 13px;
+
+          .info-label {
+            font-weight: 500;
+            color: #666;
+            min-width: 60px;
+            flex-shrink: 0;
+          }
+
+          .info-value {
+            color: #1d1d1f;
+            font-weight: 500;
+            flex: 1;
+          }
+
+          .color-preview {
+            width: 16px;
+            height: 16px;
+            border-radius: 3px;
+            border: 1px solid rgba(0, 0, 0, 0.1);
+            flex-shrink: 0;
+          }
+
+          .color-temp {
+            font-size: 11px;
+            color: #8e8e93;
+            background: rgba(142, 142, 147, 0.1);
+            padding: 2px 6px;
+            border-radius: 4px;
+          }
+
+          .material-weights {
+            display: flex;
+            gap: 6px;
+            flex-wrap: wrap;
+
+            .weight-item {
+              font-size: 11px;
+              background: rgba(52, 199, 89, 0.1);
+              color: #34c759;
+              padding: 2px 6px;
+              border-radius: 4px;
+              font-weight: 500;
+            }
+          }
+        }
+      }
+
+      .sync-placeholder {
+        display: flex;
+        align-items: center;
+        justify-content: space-between;
+        padding: 12px;
+        background: rgba(142, 142, 147, 0.05);
+        border-radius: 6px;
+        border: 1px dashed rgba(142, 142, 147, 0.3);
+
+        .placeholder-text {
+          font-size: 13px;
+          color: #8e8e93;
+        }
+
+        .sync-btn {
+          padding: 6px 12px;
+          background: #007aff;
+          color: white;
+          border: none;
+          border-radius: 4px;
+          font-size: 12px;
+          font-weight: 500;
+          cursor: pointer;
+          transition: background-color 0.2s ease;
+
+          &:hover {
+            background: #0056cc;
+          }
+
+          &:active {
+            background: #004499;
+          }
+        }
+      }
+    }
   }
-}
-.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);
-}
+  // 确认方案按钮样式 - 优化版本
+  .proposal-confirm-actions {
+    margin-top: 40px;
+    padding-top: 32px;
+    border-top: 1px solid rgba(0, 0, 0, 0.06);
+    display: flex;
+    justify-content: center;
+    position: relative;
+
+    // 添加背景装饰
+    &::before {
+      content: '';
+      position: absolute;
+      top: 0;
+      left: 50%;
+      transform: translateX(-50%);
+      width: 60px;
+      height: 3px;
+      background: linear-gradient(90deg, transparent 0%, #007aff 50%, transparent 100%);
+      border-radius: 2px;
+    }
+
+    // 使用更高特异性的选择器确保样式生效
+    .proposal-confirm-actions .confirm-proposal-btn,
+    button.confirm-proposal-btn {
+      appearance: none !important;
+      border: none !important;
+      background: linear-gradient(135deg, #007aff 0%, #0066ff 50%, #0051d5 100%) !important;
+      color: white !important;
+      padding: 16px 40px !important;
+      border-radius: 16px !important;
+      font-size: 16px !important;
+      font-weight: 600 !important;
+      cursor: pointer !important;
+      transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1) !important;
+      display: flex !important;
+      align-items: center !important;
+      gap: 10px !important;
+      box-shadow: 
+        0 8px 24px rgba(0, 122, 255, 0.25),
+        0 4px 12px rgba(0, 122, 255, 0.15),
+        inset 0 1px 0 rgba(255, 255, 255, 0.2) !important;
+      min-width: 180px !important;
+      justify-content: center !important;
+      position: relative !important;
+      overflow: hidden !important;
+      backdrop-filter: blur(10px) !important;
+      
+      // 重置可能的Material Design样式
+      text-transform: none !important;
+      letter-spacing: normal !important;
+      line-height: normal !important;
+      
+      // 添加内部光泽效果
+      &::before {
+        content: '' !important;
+        position: absolute !important;
+        top: 0 !important;
+        left: -100% !important;
+        width: 100% !important;
+        height: 100% !important;
+        background: linear-gradient(90deg, transparent 0%, rgba(255, 255, 255, 0.2) 50%, transparent 100%) !important;
+        transition: left 0.6s ease !important;
+      }
+
+      svg {
+        width: 20px !important;
+        height: 20px !important;
+        stroke-width: 2.5 !important;
+        filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.1)) !important;
+        transition: transform 0.3s ease !important;
+      }
+
+      // 悬停状态优化
+      &:hover {
+        transform: translateY(-3px) scale(1.02) !important;
+        box-shadow: 
+          0 12px 32px rgba(0, 122, 255, 0.35),
+          0 6px 16px rgba(0, 122, 255, 0.25),
+          inset 0 1px 0 rgba(255, 255, 255, 0.3) !important;
+        background: linear-gradient(135deg, #0066ff 0%, #007aff 50%, #0051d5 100%) !important;
+        
+        &::before {
+          left: 100% !important;
+        }
+        
+        svg {
+          transform: scale(1.1) rotate(5deg) !important;
+        }
+      }
+
+      // 点击状态优化
+      &:active {
+        transform: translateY(-1px) scale(0.98) !important;
+        box-shadow: 
+          0 4px 16px rgba(0, 122, 255, 0.3),
+          0 2px 8px rgba(0, 122, 255, 0.2),
+          inset 0 2px 4px rgba(0, 0, 0, 0.1) !important;
+        transition: all 0.15s ease !important;
+      }
+
+      // 焦点状态
+      &:focus {
+        outline: none !important;
+        box-shadow: 
+          0 8px 24px rgba(0, 122, 255, 0.25),
+          0 4px 12px rgba(0, 122, 255, 0.15),
+          0 0 0 3px rgba(0, 122, 255, 0.3) !important;
+      }
+
+      // 禁用状态
+       &:disabled {
+         opacity: 0.6 !important;
+         cursor: not-allowed !important;
+         transform: none !important;
+         box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1) !important;
+         
+         &:hover {
+           transform: none;
+           box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+         }
+       }
+
+       // 响应式设计
+       @media (max-width: 768px) {
+         padding: 14px 32px;
+         font-size: 15px;
+         min-width: 160px;
+         border-radius: 14px;
+         
+         svg {
+           width: 18px;
+           height: 18px;
+         }
+         
+         &:hover {
+           transform: translateY(-2px) scale(1.01);
+         }
+       }
+
+       @media (max-width: 480px) {
+         padding: 12px 24px;
+         font-size: 14px;
+         min-width: 140px;
+         border-radius: 12px;
+         gap: 8px;
+         
+         svg {
+           width: 16px;
+           height: 16px;
+         }
+         
+         &:hover {
+           transform: translateY(-1px);
+         }
+       }
+
+       // 高对比度模式支持
+       @media (prefers-contrast: high) {
+         border: 2px solid #007aff;
+         box-shadow: 
+           0 4px 12px rgba(0, 0, 0, 0.3),
+           inset 0 1px 0 rgba(255, 255, 255, 0.4);
+       }
+
+       // 减少动画模式支持
+       @media (prefers-reduced-motion: reduce) {
+         transition: none;
+         
+         &::before {
+           transition: none;
+         }
+         
+         svg {
+           transition: none;
+         }
+         
+         &:hover {
+           transform: none;
+           
+           svg {
+             transform: none;
+           }
+         }
+         
+         &:active {
+           transform: none;
+         }
+       }
+     }
+
+     // 响应式容器调整
+     @media (max-width: 768px) {
+       margin-top: 32px;
+       padding-top: 24px;
+       
+       &::before {
+         width: 50px;
+         height: 2px;
+       }
+     }
+
+     @media (max-width: 480px) {
+       margin-top: 24px;
+       padding-top: 20px;
+       
+       &::before {
+         width: 40px;
+         height: 2px;
+       }
+     }
+   }
+}
+
+/* 方案确认阶段样式 */
+.proposal-confirm-section {
+  background: white;
+  border-radius: 12px;
+  padding: 24px;
+  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
+  border: 1px solid rgba(0, 0, 0, 0.06);
+  margin-bottom: 20px;
 
-/* 进度条容器 - 支持滚动查看所有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 */
-}
+  .section-header {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    margin-bottom: 24px;
+    padding-bottom: 16px;
+    border-bottom: 1px solid rgba(0, 0, 0, 0.08);
+
+    h4 {
+      margin: 0;
+      font-size: 20px;
+      font-weight: 600;
+      color: #1d1d1f;
+      display: flex;
+      align-items: center;
+      gap: 8px;
+
+      &::before {
+        content: '✓';
+        width: 28px;
+        height: 28px;
+        background: linear-gradient(135deg, #34c759 0%, #30d158 100%);
+        color: white;
+        border-radius: 6px;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        font-size: 14px;
+        box-shadow: 0 2px 8px rgba(52, 199, 89, 0.3);
+      }
+    }
+
+    .progress-indicator {
+      display: flex;
+      align-items: center;
+      gap: 8px;
+      padding: 8px 16px;
+      background: rgba(52, 199, 89, 0.1);
+      border-radius: 20px;
+      border: 1px solid rgba(52, 199, 89, 0.2);
+
+      .progress-text {
+        font-size: 12px;
+        font-weight: 600;
+        color: #34c759;
+      }
+
+      &::after {
+        content: '✓';
+        font-size: 12px;
+        color: #34c759;
+      }
+    }
+  }
 
-.stage-progress-wrapper::-webkit-scrollbar{
-  display:none; /* Chrome, Safari, Opera */
-}
+  .requirement-info-display {
+    .info-header {
+      margin-bottom: 20px;
+
+      h5 {
+        margin: 0 0 4px 0;
+        font-size: 16px;
+        font-weight: 600;
+        color: #1d1d1f;
+      }
+
+      .info-subtitle {
+        font-size: 13px;
+        color: #8e8e93;
+      }
+    }
+
+    .info-grid {
+      display: grid;
+      grid-template-columns: repeat(auto-fit, minmax(45%, 1fr)); // 使用百分比替代固定像素
+      gap: 1.5%; // 使用百分比间距
+      
+      // 色调和材质并排布局
+        .color-material-row {
+          display: flex;
+          gap: 1.5%; // 使用百分比间距
+          
+          @media (max-width: 768px) {
+            flex-direction: column;
+            gap: 1.2%; // 移动端使用更小的百分比间距
+          }
+          
+          .color-item,
+          .material-item {
+            flex: 1;
+            min-width: 0; // 防止内容溢出
+          }
+        }
+        
+        // 空间结构和材质权重并排布局
+        .structure-weight-row {
+          display: flex;
+          gap: 1.5%; // 使用百分比间距
+          
+          @media (max-width: 768px) {
+            flex-direction: column;
+            gap: 1.2%; // 移动端使用更小的百分比间距
+          }
+          
+          .structure-item,
+          .weight-item {
+            flex: 1;
+            min-width: 0; // 防止内容溢出
+          }
+        }
+        
+        // 预设氛围和小图时间并排布局
+        .atmosphere-time-row {
+          display: flex;
+          gap: 1.5%; // 使用百分比间距
+          
+          @media (max-width: 768px) {
+            flex-direction: column;
+            gap: 1.2%; // 移动端使用更小的百分比间距
+          }
+          
+          .atmosphere-item,
+          .time-item {
+            flex: 1;
+            min-width: 0; // 防止内容溢出
+          }
+        }
+
+      .info-item {
+        background: rgba(0, 122, 255, 0.02);
+        border: 1px solid rgba(0, 122, 255, 0.08);
+        border-radius: 0.8rem; // 使用rem单位
+        padding: 1.2rem; // 使用rem单位
+        transition: all 0.2s ease;
+
+        &:hover {
+          transform: translateY(-0.15rem); // 使用rem单位
+          box-shadow: 0 0.3rem 1.2rem rgba(0, 122, 255, 0.1);
+          border-color: rgba(0, 122, 255, 0.15);
+        }
+
+        .info-label {
+          display: flex;
+          align-items: center;
+          gap: 0.6rem; // 使用rem单位
+          margin-bottom: 0.9rem; // 使用rem单位
+          font-size: 0.9rem; // 使用rem单位
+          font-weight: 600;
+          color: #1d1d1f;
+
+          svg {
+            width: 1rem; // 使用rem单位
+            height: 16px;
+            color: #007aff;
+          }
+        }
+
+        .info-content {
+          .info-value {
+            font-size: 14px;
+            color: #1d1d1f;
+            font-weight: 500;
+            display: block;
+            margin-bottom: 8px;
+          }
+
+          .color-preview {
+            width: 20px;
+            height: 20px;
+            border-radius: 4px;
+            border: 1px solid rgba(0, 0, 0, 0.1);
+            display: inline-block;
+            margin-left: 8px;
+          }
+
+          .material-list {
+            display: flex;
+            flex-wrap: wrap;
+            gap: 6px;
+
+            .material-tag {
+              background: rgba(52, 199, 89, 0.1);
+              color: #34c759;
+              padding: 4px 8px;
+              border-radius: 4px;
+              font-size: 12px;
+              font-weight: 500;
+            }
+          }
+
+          .structure-details {
+            display: flex;
+            flex-direction: column;
+            gap: 4px;
+
+            .structure-item {
+              font-size: 13px;
+              color: #666;
+              padding: 2px 0;
+            }
+          }
+
+          .weight-list {
+            display: flex;
+            flex-direction: column;
+            gap: 8px;
+
+            .weight-item {
+              display: flex;
+              justify-content: space-between;
+              align-items: center;
+              padding: 6px 12px;
+              background: white;
+              border-radius: 6px;
+              border: 1px solid rgba(0, 0, 0, 0.06);
+
+              .weight-label {
+                font-size: 13px;
+                color: #666;
+                font-weight: 500;
+              }
+
+              .weight-value {
+                font-size: 13px;
+                color: #007aff;
+                font-weight: 600;
+                background: rgba(0, 122, 255, 0.1);
+                padding: 2px 8px;
+                border-radius: 4px;
+              }
+            }
+          }
+
+          .atmosphere-info {
+            display: flex;
+            align-items: center;
+            gap: 8px;
+
+            .atmosphere-name {
+              font-size: 14px;
+              color: #1d1d1f;
+              font-weight: 600;
+            }
+
+            .color-temp {
+              font-size: 11px;
+              background: rgba(255, 149, 0, 0.1);
+              color: #ff9500;
+              padding: 2px 6px;
+              border-radius: 4px;
+            }
+          }
+
+          .time-estimate {
+            font-size: 16px;
+            color: #007aff;
+            font-weight: 600;
+            background: rgba(0, 122, 255, 0.1);
+            padding: 8px 16px;
+            border-radius: 8px;
+            display: inline-block;
+          }
+        }
+      }
+    }
+  }
 
-/* 进度条容器内部样式 */
-.stage-progress{
-  display:flex;
-  align-items:center;
-  position:relative;
-  padding:$ios-spacing-sm 0;
-  min-width:100%;
+  .requirement-pending {
+    text-align: center;
+    padding: 40px 20px;
+    background: rgba(255, 149, 0, 0.02);
+    border: 2px dashed rgba(255, 149, 0, 0.2);
+    border-radius: 12px;
+
+    .pending-icon {
+      margin-bottom: 16px;
+
+      svg {
+        color: rgba(255, 149, 0, 0.4);
+      }
+    }
+
+    h5 {
+      font-size: 16px;
+      font-weight: 600;
+      color: #1d1d1f;
+      margin: 0 0 8px 0;
+    }
+
+    p {
+      font-size: 14px;
+      color: #8e8e93;
+      line-height: 1.4;
+      margin: 0;
+    }
+  }
 }
 
-/* 进度线 */
-.stage-progress::before{
-  content:'';
-  position:absolute;
-  top:50%;
-  left:22px; /* 图标宽度的一半 */
-  right:22px;
-  height:3px;
-  background-color:$ios-border;
-  transform:translateY(-50%);
-  z-index:1;
+/* 方案确认阶段样式 */
+.proposal-confirm-stage {
+  .requirement-summary-card {
+    background: white;
+    border-radius: 12px;
+    padding: 24px;
+    box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
+    border: 1px solid rgba(0, 0, 0, 0.06);
+    margin-bottom: 20px;
+
+    .card-header {
+      display: flex;
+      align-items: center;
+      justify-content: space-between;
+      margin-bottom: 20px;
+      padding-bottom: 16px;
+      border-bottom: 1px solid rgba(0, 0, 0, 0.08);
+
+      .header-left {
+        display: flex;
+        align-items: center;
+        gap: 12px;
+
+        .status-icon {
+          width: 32px;
+          height: 32px;
+          border-radius: 8px;
+          display: flex;
+          align-items: center;
+          justify-content: center;
+          font-size: 16px;
+          background: linear-gradient(135deg, #34c759 0%, #30d158 100%);
+          color: white;
+          box-shadow: 0 2px 8px rgba(52, 199, 89, 0.3);
+        }
+
+        .header-text {
+          h3 {
+            margin: 0 0 4px 0;
+            font-size: 18px;
+            font-weight: 600;
+            color: #1d1d1f;
+          }
+
+          .subtitle {
+            font-size: 13px;
+            color: #8e8e93;
+            margin: 0;
+          }
+        }
+      }
+
+      .progress-indicator {
+        display: flex;
+        align-items: center;
+        gap: 8px;
+        padding: 8px 16px;
+        background: rgba(52, 199, 89, 0.1);
+        border-radius: 20px;
+        border: 1px solid rgba(52, 199, 89, 0.2);
+
+        .progress-text {
+          font-size: 12px;
+          font-weight: 600;
+          color: #34c759;
+        }
+
+        .check-icon {
+          font-size: 14px;
+          color: #34c759;
+        }
+      }
+    }
+
+    .requirement-info-grid {
+      display: grid;
+      grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
+      gap: 16px;
+
+      .info-section {
+        background: rgba(0, 122, 255, 0.02);
+        border: 1px solid rgba(0, 122, 255, 0.08);
+        border-radius: 10px;
+        padding: 16px;
+        transition: all 0.2s ease;
+
+        &:hover {
+          transform: translateY(-2px);
+          box-shadow: 0 4px 16px rgba(0, 122, 255, 0.1);
+          border-color: rgba(0, 122, 255, 0.15);
+        }
+
+        .section-title {
+          display: flex;
+          align-items: center;
+          gap: 8px;
+          margin-bottom: 12px;
+
+          .title-icon {
+            width: 20px;
+            height: 20px;
+            border-radius: 4px;
+            display: flex;
+            align-items: center;
+            justify-content: center;
+            font-size: 12px;
+            background: #007aff;
+            color: white;
+          }
+
+          .title-text {
+            font-size: 14px;
+            font-weight: 600;
+            color: #1d1d1f;
+          }
+        }
+
+        .info-content {
+          .info-item {
+            display: flex;
+            align-items: center;
+            justify-content: space-between;
+            padding: 10px 0;
+            border-bottom: 1px solid rgba(0, 0, 0, 0.04);
+
+            &:last-child {
+              border-bottom: none;
+              padding-bottom: 0;
+            }
+
+            .item-label {
+              font-size: 13px;
+              color: #666;
+              font-weight: 500;
+            }
+
+            .item-value {
+              font-size: 13px;
+              color: #1d1d1f;
+              font-weight: 600;
+              text-align: right;
+              max-width: 60%;
+              word-break: break-word;
+
+              &.highlight {
+                color: #007aff;
+                background: rgba(0, 122, 255, 0.1);
+                padding: 2px 8px;
+                border-radius: 4px;
+              }
+            }
+          }
+        }
+      }
+    }
+
+    .completion-placeholder {
+      text-align: center;
+      padding: 40px 20px;
+      background: rgba(255, 149, 0, 0.02);
+      border: 2px dashed rgba(255, 149, 0, 0.2);
+      border-radius: 12px;
+
+      .placeholder-icon {
+        font-size: 48px;
+        color: rgba(255, 149, 0, 0.4);
+        margin-bottom: 16px;
+      }
+
+      .placeholder-title {
+        font-size: 16px;
+        font-weight: 600;
+        color: #1d1d1f;
+        margin-bottom: 8px;
+      }
+
+      .placeholder-subtitle {
+        font-size: 14px;
+        color: #8e8e93;
+        line-height: 1.4;
+      }
+    }
+  }
 }
 
-.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;
+      
+      // 顶部导航条容器样式
+      .top-nav-container {
+        display: flex;
+        align-items: center;
+        margin-right: $ios-spacing-md; // 与导出按钮保持间距
+        
+        // 顶部导航条样式
+        .top-nav {
+          .vertical-nav {
+            display: flex;
+            flex-direction: row; // 水平排列
+            gap: 10px; // 导航按钮之间的间距
+            background: rgba(0, 122, 255, 0.05);
+            border-radius: 8px;
+            padding: 4px;
+            
+            .nav-item {
+              padding: 8px 16px;
+              border: none;
+              border-radius: 6px;
+              background: transparent;
+              color: $ios-text-primary;
+              font-size: 14px;
+              font-weight: 500;
+              cursor: pointer;
+              transition: all 0.2s ease;
+              white-space: nowrap;
+              min-height: 36px;
+              display: flex;
+              align-items: center;
+              justify-content: center;
+              
+              &:hover {
+                background: rgba(0, 122, 255, 0.1);
+                color: $ios-primary;
+              }
+              
+              &.active {
+                background: $ios-primary;
+                color: white;
+                font-weight: 600;
+              }
+              
+              .nav-text {
+                white-space: nowrap;
+              }
+            }
+          }
+        }
+      }
+      
+      .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 28%; // 使用百分比:主内容区和右侧栏(28%)
+  gap: 2%;
+  max-width: 96vw; // 使用视口单位,留出4%边距
+  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: 25%; // 使用百分比最小宽度
+    max-width: 32%; // 使用百分比最大宽度
   }
 }
 
-/* 未开始阶段样式 */
-.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: 1.5%;
+  }
 
-/* 优化客户反馈卡片内部布局 */
-.feedback-card .card-content {
-  flex: 1;
-  display: flex;
-  flex-direction: column;
+  .main-content-layout {
+    grid-template-columns: 1fr 25%; // 中等屏幕调整为25%
+    gap: 1.5%;
+  }
+  
+  .horizontal-nav-container {
+    margin-bottom: 1.5%;
+    padding: 1%;
+  }
 }
 
-.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: 1.2%;
+  }
 
-.feedback-header {
-  display: flex;
-  justify-content: space-between;
-  align-items: flex-start;
-  margin-bottom: $ios-spacing-sm;
+  .main-content-layout {
+    grid-template-columns: 1fr 23%; // 进一步减小右侧栏宽度为23%
+    gap: 1.2%;
+    
+    .right-column {
+      min-width: 22%; // 使用百分比
+      max-width: 26%; // 使用百分比
+    }
+  }
+  
+  .horizontal-nav {
+    .nav-item {
+      padding: 1% 1.5%;
+      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: 1%;
+  }
+  
+  .main-content-layout {
+    grid-template-columns: 1fr;
+    gap: 1.5%;
+    
+    .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;
+      
+      // 移动端顶部导航条样式调整
+      .top-nav-container {
+        order: -1; // 确保导航条在移动端显示在最前面
+        width: 100%;
+        margin-right: 0;
+        margin-bottom: $ios-spacing-sm;
+        
+        .top-nav {
+          .vertical-nav {
+            width: 100%;
+            justify-content: center;
+            gap: 8px; // 移动端减小间距
+            
+            .nav-item {
+              flex: 1;
+              min-height: 32px;
+              padding: 6px 12px;
+              font-size: 13px;
+              
+              .nav-text {
+                font-size: 13px;
+              }
+            }
+          }
+        }
+      }
+    }
+  }
 }
 
-/* 优化设计师变更记录卡片内部布局 */
-.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 {
+    .header-actions {
+      .top-nav-container {
+        .top-nav {
+          .vertical-nav {
+            gap: 6px;
+            padding: 3px;
+            
+            .nav-item {
+              min-height: 28px;
+              padding: 4px 8px;
+              font-size: 12px;
+              
+              .nav-text {
+                font-size: 12px;
+              }
+            }
+          }
+        }
+      }
+    }
+  }
 }
 
-.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;
-}
-
-.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;
-}
+  .main-content-layout {
+    grid-template-columns: 1fr;
+    gap: $ios-spacing-sm;
 
-.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;
+        }
+      }
+    }
+  }
+}

+ 660 - 129
src/app/pages/designer/project-detail/project-detail.ts

@@ -12,6 +12,12 @@ import {
   Settlement,
   ProjectStage
 } from '../../../models/project.model';
+import { ConsultationOrderPanelComponent } from '../../../shared/components/consultation-order-panel/consultation-order-panel.component';
+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, ConsultationOrderPanelComponent, RequirementsConfirmCardComponent, SettlementCardComponent, CustomerReviewCardComponent, ComplaintCardComponent, VerticalNavComponent],
   templateUrl: './project-detail.html',
   styleUrls: ['./project-detail.scss', './debug-styles.scss']
 })
@@ -77,7 +84,7 @@ export class ProjectDetail implements OnInit, OnDestroy {
   showDropdown: boolean = false;
   currentStage: string = '';
   // 新增:10阶段顺序(串式流程)
-  stageOrder: ProjectStage[] = ['订单创建', '需求沟通', '方案确认', '建模', '软装', '渲染', '尾款结算', '客户评价', '投诉处理'];
+  stageOrder: ProjectStage[] = ['订单创建', '需求沟通', '方案确认', '建模', '软装', '渲染', '后期', '尾款结算', '客户评价', '投诉处理'];
   // 新增:阶段展开状态(默认全部收起,当前阶段在数据加载后自动展开)
   expandedStages: Partial<Record<ProjectStage, boolean>> = {
     '订单创建': false,
@@ -86,6 +93,7 @@ export class ProjectDetail implements OnInit, OnDestroy {
     '建模': false,
     '软装': false,
     '渲染': false,
+    '后期': false,
     '尾款结算': false,
     '客户评价': false,
     '投诉处理': false,
@@ -95,7 +103,7 @@ export class ProjectDetail implements OnInit, OnDestroy {
   sections: Array<{ key: SectionKey; label: string; stages: ProjectStage[] }> = [
     { key: 'order', label: '订单创建', stages: ['订单创建'] },
     { key: 'requirements', label: '确认需求', stages: ['需求沟通', '方案确认'] },
-    { key: 'delivery', label: '交付执行', stages: ['建模', '软装', '渲染'] },
+    { key: 'delivery', label: '交付执行', stages: ['建模', '软装', '渲染', '后期'] },
     { key: 'aftercare', label: '售后', stages: ['尾款结算', '客户评价', '投诉处理'] }
   ];
   expandedSection: SectionKey | null = null;
@@ -137,11 +145,12 @@ export class ProjectDetail implements OnInit, OnDestroy {
   whiteModelImages: Array<{ id: string; name: string; url: string; size?: string; reviewStatus?: 'pending' | 'approved' | 'rejected'; synced?: boolean }> = [];
   softDecorImages: Array<{ id: string; name: string; url: string; size?: string; reviewStatus?: 'pending' | 'approved' | 'rejected'; synced?: boolean }> = [];
   renderLargeImages: Array<{ id: string; name: string; url: string; size?: string; reviewStatus?: 'pending' | 'approved' | 'rejected'; synced?: boolean }> = [];
+  postProcessImages: Array<{ id: string; name: string; url: string; size?: string; reviewStatus?: 'pending' | 'approved' | 'rejected'; synced?: boolean }> = [];
   showRenderUploadModal: boolean = false;
   pendingRenderLargeItems: Array<{ id: string; name: string; url: string; file: File }> = [];
 
   // 视图上下文:根据路由前缀识别角色视角(客服/设计师/组长)
-  private roleContext: 'customer-service' | 'designer' | 'team-leader' = 'designer';
+  roleContext: 'customer-service' | 'designer' | 'team-leader' = 'designer';
 
   constructor(
     private route: ActivatedRoute,
@@ -253,6 +262,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);
@@ -294,6 +316,15 @@ export class ProjectDetail implements OnInit, OnDestroy {
   // ============ 角色视图与只读控制(新增) ============
   private detectRoleContextFromUrl(): 'customer-service' | 'designer' | 'team-leader' {
     const url = this.router.url || '';
+    
+    // 首先检查查询参数中的role
+    const queryParams = this.route.snapshot.queryParamMap;
+    const roleParam = queryParams.get('role');
+    if (roleParam === 'customer-service') {
+      return 'customer-service';
+    }
+    
+    // 如果没有role查询参数,则根据URL路径判断
     if (url.includes('/customer-service/')) return 'customer-service';
     if (url.includes('/team-leader/')) return 'team-leader';
     return 'designer';
@@ -305,16 +336,43 @@ export class ProjectDetail implements OnInit, OnDestroy {
   // 只读规则:客服视角为只读
   isReadOnly(): boolean { return this.isCustomerServiceView(); }
 
-  // 设计师仅看三大执行阶段,其它角色看全流程
-  getVisibleStages(): ProjectStage[] {
-    if (this.isDesignerView()) {
-      return this.stageOrder.filter(s => ['建模', '软装', '渲染'].includes(s));
+  // 权限控制:客服只能编辑订单创建、确认需求、售后板块
+  canEditSection(sectionKey: SectionKey): boolean {
+    if (this.isCustomerServiceView()) {
+      return sectionKey === 'order' || sectionKey === 'requirements' || sectionKey === 'aftercare';
+    }
+    return true; // 设计师和组长可以编辑所有板块
+  }
+
+  // 权限控制:客服只能编辑特定阶段
+  canEditStage(stage: ProjectStage): boolean {
+    if (this.isCustomerServiceView()) {
+      const editableStages: ProjectStage[] = [
+        '订单创建', '需求沟通', '方案确认', // 订单创建和确认需求板块
+        '尾款结算', '客户评价', '投诉处理' // 售后板块
+      ];
+      return editableStages.includes(stage);
     }
-    return this.stageOrder;
+    return true; // 设计师和组长可以编辑所有阶段
+  }
+
+  // 计算当前激活板块:优先用户点击的 expandedSection;否则取当前阶段所属板块;再否则回退首个板块
+  private getActiveSectionKey(): SectionKey {
+    if (this.expandedSection) return this.expandedSection;
+    const current = this.project?.currentStage as ProjectStage | undefined;
+    return current ? this.getSectionKeyForStage(current) : this.sections[0].key;
+  }
+
+  // 返回当前板块的全部阶段(所有角色一致):
+  // 设计师也可查看 订单创建/确认需求/售后 板块内容
+  getVisibleStages(): ProjectStage[] {
+    const activeKey = this.getActiveSectionKey();
+    const sec = this.sections.find(s => s.key === activeKey);
+    return sec ? sec.stages : [];
   }
 
   // ============ 组长:同步上传与审核(新增,模拟实现) ============
-  syncUploadedImages(phase: 'white' | 'soft' | 'render'): void {
+  syncUploadedImages(phase: 'white' | 'soft' | 'render' | 'postProcess'): void {
     if (!this.isTeamLeaderView()) return;
     const markSynced = (arr: Array<{ reviewStatus?: 'pending'|'approved'|'rejected'; synced?: boolean }>) => {
       arr.forEach(img => {
@@ -325,10 +383,11 @@ export class ProjectDetail implements OnInit, OnDestroy {
     if (phase === 'white') markSynced(this.whiteModelImages);
     if (phase === 'soft') markSynced(this.softDecorImages);
     if (phase === 'render') markSynced(this.renderLargeImages);
+    if (phase === 'postProcess') markSynced(this.postProcessImages);
     alert('已同步该阶段的图片信息(模拟)');
   }
 
-  reviewImage(imageId: string, phase: 'white' | 'soft' | 'render', status: 'approved' | 'rejected'): void {
+  reviewImage(imageId: string, phase: 'white' | 'soft' | 'render' | 'postProcess', status: 'approved' | 'rejected'): void {
     if (!this.isTeamLeaderView()) return;
     const setStatus = (arr: Array<{ id: string; reviewStatus?: 'pending'|'approved'|'rejected'; synced?: boolean }>) => {
       const target = arr.find(i => i.id === imageId);
@@ -340,6 +399,7 @@ export class ProjectDetail implements OnInit, OnDestroy {
     if (phase === 'white') setStatus(this.whiteModelImages);
     if (phase === 'soft') setStatus(this.softDecorImages);
     if (phase === 'render') setStatus(this.renderLargeImages);
+    if (phase === 'postProcess') setStatus(this.postProcessImages);
   }
 
   getImageReviewStatusText(img: { reviewStatus?: 'pending'|'approved'|'rejected'; synced?: boolean }): string {
@@ -1119,29 +1179,178 @@ 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);
+      } else if (this.postProcessImages.find(i => i.id === this.previewImageData.id)) {
+        this.removePostProcessImage(this.previewImageData.id);
+      }
+      this.closeImagePreview();
+    }
+  }
+
+  // 拖拽事件处理
+  onDragOver(event: DragEvent): void {
+    event.preventDefault();
+    event.stopPropagation();
+    this.isDragOver = true;
+  }
+
+  onDragLeave(event: DragEvent): void {
+    event.preventDefault();
+    event.stopPropagation();
+    this.isDragOver = false;
+  }
+
+  onFileDrop(event: DragEvent, type: 'whiteModel' | 'softDecor' | 'render' | 'postProcess'): void {
+    event.preventDefault();
+    event.stopPropagation();
+    this.isDragOver = false;
+
+    const files = event.dataTransfer?.files;
+    if (!files || files.length === 0) return;
+
+    // 创建模拟的input事件
+    const mockEvent = {
+      target: {
+        files: files
+      }
+    } as any;
+
+    // 根据类型调用相应的处理方法
+    switch (type) {
+      case 'whiteModel':
+        this.onWhiteModelSelected(mockEvent);
+        break;
+      case 'softDecor':
+        this.onSoftDecorSmallPicsSelected(mockEvent);
+        break;
+      case 'render':
+        this.onRenderLargePicsSelected(mockEvent);
+        break;
+      case 'postProcess':
+        this.onPostProcessPicsSelected(mockEvent);
+        break;
+    }
+  }
+
+  // 触发文件输入框
+  triggerFileInput(type: 'whiteModel' | 'softDecor' | 'render' | 'postProcess'): void {
+    let inputId: string;
+    switch (type) {
+      case 'whiteModel':
+        inputId = 'whiteModelFileInput';
+        break;
+      case 'softDecor':
+        inputId = 'softDecorFileInput';
+        break;
+      case 'render':
+        inputId = 'renderFileInput';
+        break;
+      case 'postProcess':
+        inputId = 'postProcessFileInput';
+        break;
+    }
+    const input = document.querySelector(`#${inputId}`) as HTMLInputElement;
+    if (input) {
+      input.click();
+    }
+  }
+
   removeSoftDecorImage(id: string): void {
     const target = this.softDecorImages.find(i => i.id === id);
     if (target) this.revokeUrl(target.url);
     this.softDecorImages = this.softDecorImages.filter(i => i.id !== id);
   }
 
+  // 新增:后期阶段图片上传处理
+  async onPostProcessPicsSelected(event: Event): Promise<void> {
+    const input = event.target as HTMLInputElement;
+    if (!input.files || input.files.length === 0) return;
+    const files = Array.from(input.files).filter(f => /\.(jpg|jpeg|png)$/i.test(f.name));
+
+    for (const f of files) {
+      const item = this.makeImageItem(f);
+      this.postProcessImages.unshift({ 
+        id: item.id, 
+        name: item.name, 
+        url: item.url, 
+        size: this.formatFileSize(f.size) 
+      });
+    }
+    input.value = '';
+  }
+
+  removePostProcessImage(id: string): void {
+    const target = this.postProcessImages.find(i => i.id === id);
+    if (target) this.revokeUrl(target.url);
+    this.postProcessImages = this.postProcessImages.filter(i => i.id !== id);
+  }
+
+  // 新增:后期阶段确认上传并自动进入下一阶段(尾款结算)
+  confirmPostProcessUpload(): void {
+    if (this.postProcessImages.length === 0) return;
+    this.advanceToNextStage('后期');
+  }
+
   // 新增:软装阶段 确认上传并自动进入下一阶段(渲染)
   confirmSoftDecorUpload(): void {
     if (this.softDecorImages.length === 0) return;
     this.advanceToNextStage('软装');
   }
 
+  // 新增:渲染阶段 确认上传并自动进入下一阶段(后期)
+  confirmRenderUpload(): void {
+    if (this.renderLargeImages.length === 0) return;
+    this.advanceToNextStage('渲染');
+  }
+
   // =========== 渲染阶段:大图上传(弹窗 + 4K校验) ===========
   openRenderUploadModal(): void {
     this.showRenderUploadModal = true;
     this.pendingRenderLargeItems = [];
   }
+  
   closeRenderUploadModal(): void {
     // 关闭时释放临时预览URL
     this.pendingRenderLargeItems.forEach(i => this.revokeUrl(i.url));
     this.pendingRenderLargeItems = [];
     this.showRenderUploadModal = false;
   }
+  
   async onRenderLargePicsSelected(event: Event): Promise<void> {
     const input = event.target as HTMLInputElement;
     if (!input.files || input.files.length === 0) return;
@@ -1154,140 +1363,462 @@ 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) }));
-    this.renderLargeImages.unshift(...toAdd);
-    this.closeRenderUploadModal();
-    // 新增:渲染阶段确认后,自动进入下一阶段(后期)
-    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: '已失联' }
+  ];
+
+  // 需求关键信息同步数据
+  requirementKeyInfo = {
+    colorAtmosphere: {
+      description: '',
+      mainColor: '',
+      colorTemp: '',
+      materials: [] as string[]
+    },
+    spaceStructure: {
+      lineRatio: 0,
+      blankRatio: 0,
+      flowWidth: 0,
+      aspectRatio: 0,
+      ceilingHeight: 0
+    },
+    materialWeights: {
+      fabricRatio: 0,
+      woodRatio: 0,
+      metalRatio: 0,
+      smoothness: 0,
+      glossiness: 0
+    },
+    presetAtmosphere: {
+      name: '',
+      rgb: '',
+      colorTemp: '',
+      materials: [] as string[]
+    }
+  };
+
+  // 客户信息:搜索/选择/清空/同步/快速填写 逻辑
+  searchCustomer(): void {
+    if (this.customerSearchKeyword.trim().length >= 2) {
+      this.customerSearchResults = [
+        { id: '1', name: '张先生', phone: '138****5678', customerType: '老客户', source: '官网咨询', avatar: "data:image/svg+xml,%3Csvg width='64' height='40' xmlns='http://www.w3.org/2000/svg'%3E%3Crect width='100%25' height='100%25' fill='%23E6E6E6'/%3E%3Ctext x='50%25' y='50%25' font-family='Arial' font-size='13.333333333333334' font-weight='bold' text-anchor='middle' fill='%23555555' dy='0.3em'%3EIMG%3C/text%3E%3C/svg%3E" },
+        { id: '2', name: '李女士', phone: '139****1234', customerType: 'VIP客户', source: '推荐介绍', avatar: "data:image/svg+xml,%3Csvg width='65' height='40' xmlns='http://www.w3.org/2000/svg'%3E%3Crect width='100%25' height='100%25' fill='%23DCDCDC'/%3E%3Ctext x='50%25' y='50%25' font-family='Arial' font-size='13.333333333333334' font-weight='bold' text-anchor='middle' fill='%23555555' dy='0.3em'%3EIMG%3C/text%3E%3C/svg%3E" }
+      ];
+    } else {
+      this.customerSearchResults = [];
+    }
+  }
+
+  selectCustomer(customer: { id: string; name: string; phone: string; wechat?: string; avatar?: string; customerType?: string; source?: string; remark?: string }): void {
+    this.selectedOrderCustomer = customer;
+    this.customerForm.patchValue({
+      name: customer.name,
+      phone: customer.phone,
+      wechat: customer.wechat || '',
+      customerType: customer.customerType || '新客户',
+      source: customer.source || '',
+      remark: customer.remark || ''
+    });
+    this.customerSearchResults = [];
+    this.customerSearchKeyword = '';
+  }
+
+  clearSelectedCustomer(): void {
+    this.selectedOrderCustomer = null;
+    this.customerForm.reset({ customerType: '新客户' });
+  }
+
+  quickFillCustomerInfo(keyword: string): void {
+    const k = (keyword || '').trim();
+    if (!k) return;
+    // 模拟:若有搜索结果,选择第一条
+    if (this.customerSearchResults.length === 0) this.searchCustomer();
+    if (this.customerSearchResults.length > 0) {
+      this.selectCustomer(this.customerSearchResults[0]);
+    }
+  }
+
+  syncMiniprogramCustomerInfo(): void {
+    if (this.isSyncing) return;
+    this.isSyncing = true;
+    setTimeout(() => {
+      // 模拟从小程序同步到客户表单
+      this.customerForm.patchValue({
+        name: '小程序用户',
+        phone: '13800001234',
+        wechat: 'wx_user_001',
+        customerType: '新客户',
+        source: '小程序下单'
+      });
+      this.isSyncing = false;
+    }, 1000);
+  }
+
+  downloadFile(file: ProjectFile): void {
+    // 实现文件下载逻辑
+    const link = document.createElement('a');
+    link.href = file.url;
+    link.download = file.name;
+    link.click();
+  }
+
+  previewFile(file: ProjectFile): void {
+    // 预览文件逻辑
+    console.log('预览文件:', file.name);
+  }
+
+  // 同步需求关键信息到客户信息卡片
+  syncRequirementKeyInfo(requirementData: any): void {
+    if (requirementData) {
+      // 同步色彩氛围信息
+      if (requirementData.colorIndicators) {
+        this.requirementKeyInfo.colorAtmosphere = {
+          description: requirementData.colorIndicators.colorRange || '',
+          mainColor: `rgb(${requirementData.colorIndicators.mainColor?.r || 0}, ${requirementData.colorIndicators.mainColor?.g || 0}, ${requirementData.colorIndicators.mainColor?.b || 0})`,
+          colorTemp: `${requirementData.colorIndicators.colorTemperature || 0}K`,
+          materials: []
+        };
+      }
+
+      // 同步空间结构信息
+      if (requirementData.spaceIndicators) {
+        this.requirementKeyInfo.spaceStructure = {
+          lineRatio: requirementData.spaceIndicators.lineRatio || 0,
+          blankRatio: requirementData.spaceIndicators.blankRatio || 0,
+          flowWidth: requirementData.spaceIndicators.flowWidth || 0,
+          aspectRatio: requirementData.spaceIndicators.aspectRatio || 0,
+          ceilingHeight: requirementData.spaceIndicators.ceilingHeight || 0
+        };
+      }
+
+      // 同步材质权重信息
+      if (requirementData.materialIndicators) {
+        this.requirementKeyInfo.materialWeights = {
+          fabricRatio: requirementData.materialIndicators.fabricRatio || 0,
+          woodRatio: requirementData.materialIndicators.woodRatio || 0,
+          metalRatio: requirementData.materialIndicators.metalRatio || 0,
+          smoothness: requirementData.materialIndicators.smoothness || 0,
+          glossiness: requirementData.materialIndicators.glossiness || 0
+        };
+      }
+
+      // 同步预设氛围信息
+      if (requirementData.selectedPresetAtmosphere) {
+        this.requirementKeyInfo.presetAtmosphere = {
+          name: requirementData.selectedPresetAtmosphere.name || '',
+          rgb: requirementData.selectedPresetAtmosphere.rgb || '',
+          colorTemp: requirementData.selectedPresetAtmosphere.colorTemp || '',
+          materials: requirementData.selectedPresetAtmosphere.materials || []
+        };
+      }
+
+      console.log('需求关键信息已同步:', this.requirementKeyInfo);
+    } else {
+      // 模拟数据用于演示
+      this.requirementKeyInfo = {
+        colorAtmosphere: {
+          description: '温馨暖调',
+          mainColor: 'rgb(255, 230, 180)',
+          colorTemp: '2700K',
+          materials: ['木质', '布艺']
+        },
+        spaceStructure: {
+          lineRatio: 60,
+          blankRatio: 30,
+          flowWidth: 0.9,
+          aspectRatio: 1.6,
+          ceilingHeight: 2.8
+        },
+        materialWeights: {
+          fabricRatio: 50,
+          woodRatio: 30,
+          metalRatio: 20,
+          smoothness: 7,
+          glossiness: 4
+        },
+        presetAtmosphere: {
+          name: '现代简约',
+          rgb: '200,220,240',
+          colorTemp: '5000K',
+          materials: ['金属', '玻璃']
+        }
+      };
+    }
+  }
+
+  // 新增:处理需求阶段完成事件
+  onRequirementsStageCompleted(event: { stage: string; allStagesCompleted: boolean }): void {
+    console.log('需求阶段完成事件:', event);
+    
+    if (event.allStagesCompleted && event.stage === 'requirements-communication') {
+      // 自动推进到方案确认阶段
+      this.currentStage = '方案确认';
+      this.expandedStages['方案确认'] = true;
+      this.expandedStages['需求沟通'] = false;
+      
+      // 更新项目状态
+      this.updateProjectStage('方案确认');
+      
+      console.log('自动推进到方案确认阶段');
+    }
+  }
+
+  // 新增:确认方案方法
+  confirmProposal(): void {
+    console.log('确认方案按钮被点击');
+    
+    // 自动跳转到建模阶段
+    this.currentStage = '建模';
+    this.expandedStages['建模'] = true;
+    this.expandedStages['方案确认'] = false;
+    
+    // 更新项目状态
+    this.updateProjectStage('建模');
+    
+    // 滚动到建模阶段
+    this.scrollToStage('建模');
+    
+    console.log('已跳转到建模阶段');
+  }
+
+  // 获取同步的关键信息摘要
+  getRequirementSummary(): string[] {
+    const summary: string[] = [];
+    
+    if (this.requirementKeyInfo.colorAtmosphere.description) {
+      summary.push(`色彩氛围: ${this.requirementKeyInfo.colorAtmosphere.description}`);
+    }
+    
+    if (this.requirementKeyInfo.spaceStructure.aspectRatio > 0) {
+      summary.push(`空间比例: ${this.requirementKeyInfo.spaceStructure.aspectRatio.toFixed(1)}`);
+    }
+    
+    if (this.requirementKeyInfo.materialWeights.woodRatio > 0) {
+      summary.push(`木质占比: ${this.requirementKeyInfo.materialWeights.woodRatio}%`);
+    }
+    
+    if (this.requirementKeyInfo.presetAtmosphere.name) {
+      summary.push(`预设氛围: ${this.requirementKeyInfo.presetAtmosphere.name}`);
+    }
+    
+    return summary;
+  }
+
+  // 检查必需阶段是否全部完成(流程进度 > 确认需求 > 需求沟通四个流程)
+  areRequiredStagesCompleted(): boolean {
+    // 这里需要根据实际的需求确认流程来判断
+    // 暂时使用需求关键信息是否有数据来判断
+    return this.getRequirementSummary().length > 0 && 
+           (!!this.requirementKeyInfo.colorAtmosphere.description || 
+            this.requirementKeyInfo.spaceStructure.aspectRatio > 0 ||
+            this.requirementKeyInfo.materialWeights.woodRatio > 0 ||
+            !!this.requirementKeyInfo.presetAtmosphere.name);
+  }
+
+  // 获取必需阶段的完成进度百分比
+  getRequiredStagesProgress(): number {
+    let completedCount = 0;
+    const totalCount = 4; // 四个必需流程
+
+    // 检查各个关键信息是否已确认
+    if (this.requirementKeyInfo.colorAtmosphere.description) completedCount++;
+    if (this.requirementKeyInfo.spaceStructure.aspectRatio > 0) completedCount++;
+    if (this.requirementKeyInfo.materialWeights.woodRatio > 0 || 
+        this.requirementKeyInfo.materialWeights.fabricRatio > 0 || 
+        this.requirementKeyInfo.materialWeights.metalRatio > 0) completedCount++;
+    if (this.requirementKeyInfo.presetAtmosphere.name) completedCount++;
+
+    return Math.round((completedCount / totalCount) * 100);
+  }
+
+  // 获取预估小图时间
+  getEstimatedSmallImageTime(): string {
+    // 根据需求复杂度计算预估时间
+    const baseTime = 2; // 基础时间2小时
+    let additionalTime = 0;
+
+    // 根据材质复杂度增加时间
+    if (this.requirementKeyInfo.materialWeights.woodRatio > 0) additionalTime += 0.5;
+    if (this.requirementKeyInfo.materialWeights.fabricRatio > 0) additionalTime += 0.5;
+    if (this.requirementKeyInfo.materialWeights.metalRatio > 0) additionalTime += 0.5;
+
+    // 根据空间复杂度增加时间
+    if (this.requirementKeyInfo.spaceStructure.aspectRatio > 2) additionalTime += 1;
+
+    const totalTime = baseTime + additionalTime;
+    return `${totalTime}小时`;
+  }
+
+  // 处理咨询订单表单提交
+  // 存储订单创建时的客户信息和需求信息
+  orderCreationData: any = null;
+
+  onConsultationOrderSubmit(formData: any): void {
+    console.log('咨询订单表单提交:', formData);
+    
+    // 保存订单创建数据
+    this.orderCreationData = formData;
+    
+    // 根据角色上下文处理数据同步
+    if (formData.roleContext === 'customer-service') {
+      // 客服端创建的订单需要同步到设计师端
+      this.syncOrderToDesignerView(formData);
+    }
+    
+    // 更新项目阶段到下一个阶段(需求沟通)
+    this.updateProjectStage('需求沟通');
+    
+    // 展开下一个阶段
+    this.expandedStages['需求沟通'] = true;
+    this.expandedStages['订单创建'] = false;
+    
+    // 滚动到下一个阶段
+    setTimeout(() => {
+      this.scrollToStage('需求沟通');
+    }, 100);
+    
+    // 显示成功提示
+    console.log('订单创建成功,已跳转到需求沟通阶段');
+  }
+
+  // 新增:同步订单数据到设计师视图
+  private syncOrderToDesignerView(formData: any): void {
+    // 创建项目数据
+    const projectData = {
+      customerId: formData.customerInfo.id || 'customer-' + Date.now(),
+      customerName: formData.customerInfo.name,
+      requirement: formData.requirementInfo,
+      referenceCases: [],
+      tags: {
+        demandType: formData.customerInfo.demandType,
+        preferenceTags: formData.preferenceTags,
+        followUpStatus: formData.customerInfo.followUpStatus
+      }
+    };
+
+    // 调用项目服务创建项目
+    this.projectService.createProject(projectData).subscribe(
+      result => {
+        if (result.success) {
+          console.log('订单数据已同步到设计师端,项目ID:', result.projectId);
+          // 可以在这里添加成功提示或其他处理逻辑
+        }
+      },
+      error => {
+        console.error('同步订单数据到设计师端失败:', error);
+      }
+    );
+  }
 }

+ 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 {

+ 125 - 1
src/app/pages/team-leader/dashboard/dashboard.html

@@ -317,4 +317,128 @@
       </div>
     </div>
   }
-</main>
+</main>
+
+<!-- 员工详情面板 -->
+@if (showEmployeeDetailPanel && selectedEmployeeDetail) {
+  <div class="employee-detail-overlay" (click)="closeEmployeeDetailPanel()">
+    <div class="employee-detail-panel" (click)="$event.stopPropagation()">
+      <!-- 面板头部 -->
+      <div class="panel-header">
+        <h3 class="panel-title">
+          <svg class="icon-user" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
+            <path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path>
+            <circle cx="12" cy="7" r="4"></circle>
+          </svg>
+          {{ selectedEmployeeDetail.name }} 详情
+        </h3>
+        <button class="btn-close" (click)="closeEmployeeDetailPanel()">
+          <svg 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="panel-content">
+        <!-- 负载概况栏 -->
+        <div class="section workload-section">
+          <div class="section-header">
+            <svg class="section-icon" 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>
+              <line x1="9" y1="9" x2="15" y2="9"></line>
+              <line x1="9" y1="15" x2="15" y2="15"></line>
+            </svg>
+            <h4>负载概况</h4>
+          </div>
+          <div class="workload-info">
+            <div class="workload-stat">
+              <span class="stat-label">当前负责项目数:</span>
+              <span class="stat-value" [class]="selectedEmployeeDetail.currentProjects >= 3 ? 'high-workload' : 'normal-workload'">
+                {{ selectedEmployeeDetail.currentProjects }} 个
+              </span>
+            </div>
+            @if (selectedEmployeeDetail.projectNames.length > 0) {
+              <div class="project-list">
+                <span class="project-label">核心项目:</span>
+                <div class="project-tags">
+                  @for (projectName of selectedEmployeeDetail.projectNames; track $index) {
+                    <span class="project-tag">{{ projectName }}</span>
+                  }
+                  @if (selectedEmployeeDetail.currentProjects > selectedEmployeeDetail.projectNames.length) {
+                    <span class="project-tag more">+{{ selectedEmployeeDetail.currentProjects - selectedEmployeeDetail.projectNames.length }}</span>
+                  }
+                </div>
+              </div>
+            }
+          </div>
+        </div>
+
+        <!-- 请假明细栏 -->
+        <div class="section leave-section">
+          <div class="section-header">
+            <svg class="section-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
+              <rect x="3" y="4" width="18" height="18" rx="2" ry="2"></rect>
+              <line x1="16" y1="2" x2="16" y2="6"></line>
+              <line x1="8" y1="2" x2="8" y2="6"></line>
+              <line x1="3" y1="10" x2="21" y2="10"></line>
+            </svg>
+            <h4>请假明细(未来7天)</h4>
+          </div>
+          <div class="leave-table">
+            @if (selectedEmployeeDetail.leaveRecords.length > 0) {
+              <table>
+                <thead>
+                  <tr>
+                    <th>日期</th>
+                    <th>状态</th>
+                    <th>备注</th>
+                  </tr>
+                </thead>
+                <tbody>
+                  @for (record of selectedEmployeeDetail.leaveRecords; track record.id) {
+                    <tr [class]="record.isLeave ? 'leave-day' : 'work-day'">
+                      <td>{{ record.date | date:'M月d日' }}</td>
+                      <td>
+                        <span class="status-badge" [class]="record.isLeave ? 'leave' : 'work'">
+                          {{ record.isLeave ? '请假' : '正常' }}
+                        </span>
+                      </td>
+                      <td>{{ record.isLeave ? getLeaveTypeText(record.leaveType) : '-' }}</td>
+                    </tr>
+                  }
+                </tbody>
+              </table>
+            } @else {
+              <div class="no-leave">
+                <svg class="no-data-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
+                  <circle cx="12" cy="12" r="10"></circle>
+                  <path d="M8 14s1.5 2 4 2 4-2 4-2"></path>
+                  <line x1="9" y1="9" x2="9.01" y2="9"></line>
+                  <line x1="15" y1="9" x2="15.01" y2="9"></line>
+                </svg>
+                <p>未来7天无请假安排</p>
+              </div>
+            }
+          </div>
+        </div>
+
+        <!-- 红色标记说明 -->
+        <div class="section explanation-section">
+          <div class="section-header">
+            <svg class="section-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
+              <circle cx="12" cy="12" r="10"></circle>
+              <line x1="12" y1="8" x2="12" y2="12"></line>
+              <line x1="12" y1="16" x2="12.01" y2="16"></line>
+            </svg>
+            <h4>红色标记说明</h4>
+          </div>
+          <div class="explanation-content">
+            <p class="explanation-text">{{ selectedEmployeeDetail.redMarkExplanation }}</p>
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+}

+ 357 - 0
src/app/pages/team-leader/dashboard/dashboard.scss

@@ -1045,4 +1045,361 @@
   .project-kanban {
     .kanban-header, .kanban-body { grid-template-columns: 1fr; }
   }
+}
+
+/* 员工详情面板样式 */
+.employee-detail-overlay {
+  position: fixed;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  background: rgba(0, 0, 0, 0.5);
+  backdrop-filter: blur(4px);
+  z-index: 1000;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  padding: 20px;
+  animation: fadeIn 0.3s ease-out;
+}
+
+.employee-detail-panel {
+  background: #ffffff;
+  border-radius: 16px;
+  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.15);
+  max-width: 600px;
+  width: 100%;
+  max-height: 80vh;
+  overflow: hidden;
+  animation: slideUp 0.3s ease-out;
+  
+  .panel-header {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    padding: 24px 24px 16px;
+    border-bottom: 1px solid #f1f5f9;
+    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+    color: white;
+    
+    .panel-title {
+      display: flex;
+      align-items: center;
+      gap: 12px;
+      margin: 0;
+      font-size: 20px;
+      font-weight: 600;
+      
+      .icon-user {
+        width: 24px;
+        height: 24px;
+        stroke-width: 2;
+      }
+    }
+    
+    .btn-close {
+      background: rgba(255, 255, 255, 0.2);
+      border: none;
+      border-radius: 8px;
+      width: 36px;
+      height: 36px;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      cursor: pointer;
+      transition: all 0.2s ease;
+      
+      svg {
+        width: 18px;
+        height: 18px;
+        stroke: white;
+      }
+      
+      &:hover {
+        background: rgba(255, 255, 255, 0.3);
+        transform: scale(1.05);
+      }
+    }
+  }
+  
+  .panel-content {
+    padding: 24px;
+    max-height: calc(80vh - 100px);
+    overflow-y: auto;
+    
+    .section {
+      margin-bottom: 24px;
+      
+      &:last-child {
+        margin-bottom: 0;
+      }
+      
+      .section-header {
+        display: flex;
+        align-items: center;
+        gap: 8px;
+        margin-bottom: 16px;
+        
+        .section-icon {
+          width: 20px;
+          height: 20px;
+          stroke: #667eea;
+          stroke-width: 2;
+        }
+        
+        h4 {
+          margin: 0;
+          font-size: 16px;
+          font-weight: 600;
+          color: #1e293b;
+        }
+      }
+    }
+    
+    // 负载概况样式
+    .workload-section {
+      .workload-info {
+        background: #f8fafc;
+        border-radius: 12px;
+        padding: 20px;
+        border: 1px solid #e2e8f0;
+        
+        .workload-stat {
+          display: flex;
+          align-items: center;
+          gap: 8px;
+          margin-bottom: 16px;
+          
+          .stat-label {
+            font-size: 14px;
+            color: #64748b;
+            font-weight: 500;
+          }
+          
+          .stat-value {
+            font-size: 18px;
+            font-weight: 700;
+            padding: 4px 12px;
+            border-radius: 20px;
+            
+            &.normal-workload {
+              color: #059669;
+              background: #d1fae5;
+            }
+            
+            &.high-workload {
+              color: #dc2626;
+              background: #fee2e2;
+            }
+          }
+        }
+        
+        .project-list {
+          .project-label {
+            font-size: 14px;
+            color: #64748b;
+            font-weight: 500;
+            margin-bottom: 8px;
+            display: block;
+          }
+          
+          .project-tags {
+            display: flex;
+            flex-wrap: wrap;
+            gap: 8px;
+            
+            .project-tag {
+              background: #667eea;
+              color: white;
+              padding: 4px 12px;
+              border-radius: 16px;
+              font-size: 12px;
+              font-weight: 500;
+              
+              &.more {
+                background: #94a3b8;
+              }
+            }
+          }
+        }
+      }
+    }
+    
+    // 请假明细样式
+    .leave-section {
+      .leave-table {
+        background: #ffffff;
+        border-radius: 12px;
+        border: 1px solid #e2e8f0;
+        overflow: hidden;
+        
+        table {
+          width: 100%;
+          border-collapse: collapse;
+          
+          thead {
+            background: #f8fafc;
+            
+            th {
+              padding: 12px 16px;
+              text-align: left;
+              font-size: 14px;
+              font-weight: 600;
+              color: #374151;
+              border-bottom: 1px solid #e5e7eb;
+            }
+          }
+          
+          tbody {
+            tr {
+              transition: background-color 0.2s ease;
+              
+              &:hover {
+                background: #f9fafb;
+              }
+              
+              &.leave-day {
+                background: #fef2f2;
+                
+                &:hover {
+                  background: #fee2e2;
+                }
+              }
+              
+              td {
+                padding: 12px 16px;
+                font-size: 14px;
+                color: #374151;
+                border-bottom: 1px solid #f1f5f9;
+                
+                &:last-child {
+                  border-bottom: none;
+                }
+              }
+            }
+            
+            tr:last-child td {
+              border-bottom: none;
+            }
+          }
+          
+          .status-badge {
+            padding: 4px 8px;
+            border-radius: 12px;
+            font-size: 12px;
+            font-weight: 500;
+            
+            &.work {
+              background: #d1fae5;
+              color: #059669;
+            }
+            
+            &.leave {
+              background: #fee2e2;
+              color: #dc2626;
+            }
+          }
+        }
+        
+        .no-leave {
+          padding: 40px 20px;
+          text-align: center;
+          color: #64748b;
+          
+          .no-data-icon {
+            width: 48px;
+            height: 48px;
+            margin: 0 auto 16px;
+            stroke: #94a3b8;
+            stroke-width: 1.5;
+          }
+          
+          p {
+            margin: 0;
+            font-size: 14px;
+          }
+        }
+      }
+    }
+    
+    // 红色标记说明样式
+    .explanation-section {
+      .explanation-content {
+        background: linear-gradient(135deg, #fef3c7 0%, #fde68a 100%);
+        border: 1px solid #f59e0b;
+        border-radius: 12px;
+        padding: 16px;
+        
+        .explanation-text {
+          margin: 0;
+          font-size: 14px;
+          color: #92400e;
+          line-height: 1.5;
+          font-weight: 500;
+        }
+      }
+    }
+  }
+}
+
+// 动画效果
+@keyframes fadeIn {
+  from {
+    opacity: 0;
+  }
+  to {
+    opacity: 1;
+  }
+}
+
+@keyframes slideUp {
+  from {
+    opacity: 0;
+    transform: translateY(30px) scale(0.95);
+  }
+  to {
+    opacity: 1;
+    transform: translateY(0) scale(1);
+  }
+}
+
+// 响应式设计
+@media (max-width: 768px) {
+  .employee-detail-overlay {
+    padding: 10px;
+    
+    .employee-detail-panel {
+      max-width: 100%;
+      max-height: 90vh;
+      
+      .panel-header {
+        padding: 20px 20px 12px;
+        
+        .panel-title {
+          font-size: 18px;
+        }
+      }
+      
+      .panel-content {
+        padding: 20px;
+        
+        .section {
+          margin-bottom: 20px;
+        }
+        
+        .workload-section .workload-info {
+          padding: 16px;
+        }
+        
+        .leave-section .leave-table table {
+          font-size: 13px;
+          
+          th, td {
+            padding: 10px 12px;
+          }
+        }
+      }
+    }
+  }
 }

+ 269 - 2
src/app/pages/team-leader/dashboard/dashboard.ts

@@ -52,6 +52,25 @@ interface TodoTask {
   targetId: string;
 }
 
+// 员工请假记录接口
+interface LeaveRecord {
+  id: string;
+  employeeName: string;
+  date: string; // YYYY-MM-DD 格式
+  isLeave: boolean;
+  leaveType?: 'sick' | 'personal' | 'annual' | 'other'; // 请假类型
+  reason?: string; // 请假原因
+}
+
+// 员工详情面板数据接口
+interface EmployeeDetail {
+  name: string;
+  currentProjects: number; // 当前负责项目数
+  projectNames: string[]; // 项目名称列表(用于显示)
+  leaveRecords: LeaveRecord[]; // 未来7天请假记录
+  redMarkExplanation: string; // 红色标记说明
+}
+
 declare const echarts: any;
 @Component({
   selector: 'app-dashboard',
@@ -134,6 +153,45 @@ export class Dashboard implements OnInit, OnDestroy {
   ganttScale: 'week' | 'month' = 'week';
   // 新增:甘特模式(项目 / 设计师排班)
   ganttMode: 'project' | 'designer' = 'project';
+
+  // 个人详情面板相关属性
+  showEmployeeDetailPanel: boolean = false;
+  selectedEmployeeDetail: EmployeeDetail | null = null;
+  
+  // 员工请假数据(模拟数据)
+  private leaveRecords: LeaveRecord[] = [
+    { id: '1', employeeName: '张三', date: '2024-01-20', isLeave: true, leaveType: 'personal', reason: '事假' },
+    { id: '2', employeeName: '张三', date: '2024-01-21', isLeave: false },
+    { id: '3', employeeName: '张三', date: '2024-01-22', isLeave: false },
+    { id: '4', employeeName: '张三', date: '2024-01-23', isLeave: false },
+    { id: '5', employeeName: '张三', date: '2024-01-24', isLeave: false },
+    { id: '6', employeeName: '张三', date: '2024-01-25', isLeave: false },
+    { id: '7', employeeName: '张三', date: '2024-01-26', isLeave: false },
+    
+    { id: '8', employeeName: '李四', date: '2024-01-20', isLeave: false },
+    { id: '9', employeeName: '李四', date: '2024-01-21', isLeave: true, leaveType: 'sick', reason: '病假' },
+    { id: '10', employeeName: '李四', date: '2024-01-22', isLeave: true, leaveType: 'sick', reason: '病假' },
+    { id: '11', employeeName: '李四', date: '2024-01-23', isLeave: false },
+    { id: '12', employeeName: '李四', date: '2024-01-24', isLeave: false },
+    { id: '13', employeeName: '李四', date: '2024-01-25', isLeave: false },
+    { id: '14', employeeName: '李四', date: '2024-01-26', isLeave: false },
+    
+    { id: '15', employeeName: '王五', date: '2024-01-20', isLeave: false },
+    { id: '16', employeeName: '王五', date: '2024-01-21', isLeave: false },
+    { id: '17', employeeName: '王五', date: '2024-01-22', isLeave: false },
+    { id: '18', employeeName: '王五', date: '2024-01-23', isLeave: true, leaveType: 'annual', reason: '年假' },
+    { id: '19', employeeName: '王五', date: '2024-01-24', isLeave: false },
+    { id: '20', employeeName: '王五', date: '2024-01-25', isLeave: false },
+    { id: '21', employeeName: '王五', date: '2024-01-26', isLeave: false },
+    
+    { id: '22', employeeName: '赵六', date: '2024-01-20', isLeave: false },
+    { id: '23', employeeName: '赵六', date: '2024-01-21', isLeave: false },
+    { id: '24', employeeName: '赵六', date: '2024-01-22', isLeave: false },
+    { id: '25', employeeName: '赵六', date: '2024-01-23', isLeave: false },
+    { id: '26', employeeName: '赵六', date: '2024-01-24', isLeave: false },
+    { id: '27', employeeName: '赵六', date: '2024-01-25', isLeave: false },
+    { id: '28', employeeName: '赵六', date: '2024-01-26', isLeave: false }
+  ];
   constructor(private projectService: ProjectService, private router: Router) {}
 
   ngOnInit(): void {
@@ -658,6 +716,21 @@ export class Dashboard implements OnInit, OnDestroy {
     const el = this.ganttChartRef.nativeElement;
     if (!this.ganttChart) {
       this.ganttChart = echarts.init(el);
+      
+      // 添加点击事件监听器
+      this.ganttChart.on('click', (params: any) => {
+        if (params.componentType === 'series' && params.seriesType === 'custom') {
+          // 获取点击的员工名称(从y轴类目数据中获取)
+          const yAxisData = this.ganttChart.getOption().yAxis[0].data;
+          if (yAxisData && params.dataIndex !== undefined) {
+            const employeeName = yAxisData[params.value[0]];
+            if (employeeName && employeeName !== '未分配') {
+              this.onEmployeeClick(employeeName);
+            }
+          }
+        }
+      });
+      
       window.addEventListener('resize', () => {
         this.ganttChart && this.ganttChart.resize();
       });
@@ -757,6 +830,9 @@ export class Dashboard implements OnInit, OnDestroy {
     const preservedStart = typeof prevOpt?.dataZoom?.[0]?.start === 'number' ? prevOpt.dataZoom[0].start : 0;
     const preservedEnd = typeof prevOpt?.dataZoom?.[0]?.end === 'number' ? prevOpt.dataZoom[0].end : defaultEndPercent;
 
+    // 生成请假覆盖层数据
+    const leaveOverlayData = this.generateLeaveOverlayData(categories, xMin, xMax);
+
     const option = {
       backgroundColor: 'transparent',
       tooltip: {
@@ -804,8 +880,10 @@ export class Dashboard implements OnInit, OnDestroy {
         { type: 'inside', yAxisIndex: 0, zoomOnMouseWheel: true, moveOnMouseMove: true, moveOnMouseWheel: true }
       ],
       series: [
+        // 项目条形图系列
         {
           type: 'custom',
+          name: '项目进度',
           renderItem: (params: any, api: any) => {
             const categoryIndex = api.value(0);
             const start = api.coord([api.value(1), categoryIndex]);
@@ -835,6 +913,34 @@ export class Dashboard implements OnInit, OnDestroy {
             label: { formatter: '今日', color: '#ef4444', fontSize: 10, position: 'end' },
             data: [ { xAxis: todayTs } ]
           }
+        },
+        // 请假覆盖层系列
+        {
+          type: 'custom',
+          name: '请假/繁忙标记',
+          renderItem: (params: any, api: any) => {
+            const categoryIndex = api.value(0);
+            const start = api.coord([api.value(1), categoryIndex]);
+            const end = api.coord([api.value(2), categoryIndex]);
+            const height = Math.max(api.size([0, 1])[1] * 0.8, 12); // 稍微高一点,覆盖项目条
+            const rectShape = echarts.graphic.clipRectByRect({
+              x: start[0],
+              y: start[1] - height / 2,
+              width: Math.max(end[0] - start[0], 2),
+              height
+            }, {
+              x: params.coordSys.x,
+              y: params.coordSys.y,
+              width: params.coordSys.width,
+              height: params.coordSys.height
+            });
+            return rectShape ? { type: 'rect', shape: rectShape, style: api.style() } : undefined;
+          },
+          encode: { x: [1, 2], y: 0 },
+          data: leaveOverlayData,
+          itemStyle: { borderRadius: 4 },
+          emphasis: { focus: 'self' },
+          z: 10 // 确保覆盖层在项目条之上
         }
       ]
     };
@@ -1127,8 +1233,23 @@ export class Dashboard implements OnInit, OnDestroy {
 
   // 查看项目详情
   viewProjectDetails(projectId: string): void {
-    // 改为跳转到复用的项目详情(组长上下文,具备审核权限)
-    this.router.navigate(['/team-leader/project-detail', projectId]);
+    // 检测是否在iframe中运行(即从客服端访问)
+    const isInIframe = window.self !== window.top;
+    
+    if (isInIframe) {
+      // 如果在iframe中,跳转到设计师端项目详情页面,并传递客服角色标识
+      // 使用parent.window来在父窗口中进行导航
+      const targetUrl = `/designer/project-detail/${projectId}?role=customer-service`;
+      if (window.parent) {
+        window.parent.postMessage({
+          type: 'navigate',
+          url: targetUrl
+        }, '*');
+      }
+    } else {
+      // 正常情况下跳转到组长端项目详情页面
+      this.router.navigate(['/team-leader/project-detail', projectId]);
+    }
   }
 
   // 快速分配项目(增强:加入智能推荐)
@@ -1289,4 +1410,150 @@ export class Dashboard implements OnInit, OnDestroy {
     this.selectedStatus = 'all';
     this.applyFilters();
   }
+
+  // 处理甘特图员工点击事件
+  onEmployeeClick(employeeName: string): void {
+    if (!employeeName || employeeName === '未分配') {
+      return;
+    }
+    
+    // 生成员工详情数据
+    this.selectedEmployeeDetail = this.generateEmployeeDetail(employeeName);
+    this.showEmployeeDetailPanel = true;
+  }
+
+  // 生成员工详情数据
+  private generateEmployeeDetail(employeeName: string): EmployeeDetail {
+    // 获取该员工负责的项目
+    const employeeProjects = this.filteredProjects.filter(p => p.designerName === employeeName);
+    const currentProjects = employeeProjects.length;
+    const projectNames = employeeProjects.slice(0, 3).map(p => p.name); // 最多显示3个项目名称
+    
+    // 获取该员工的请假记录(未来7天)
+    const today = new Date();
+    const next7Days = Array.from({ length: 7 }, (_, i) => {
+      const date = new Date(today);
+      date.setDate(today.getDate() + i);
+      return date.toISOString().split('T')[0]; // YYYY-MM-DD 格式
+    });
+    
+    const employeeLeaveRecords = this.leaveRecords.filter(record => 
+      record.employeeName === employeeName && next7Days.includes(record.date)
+    );
+    
+    // 生成红色标记说明
+    const redMarkExplanation = this.generateRedMarkExplanation(employeeName, employeeLeaveRecords, currentProjects);
+    
+    return {
+      name: employeeName,
+      currentProjects,
+      projectNames,
+      leaveRecords: employeeLeaveRecords,
+      redMarkExplanation
+    };
+  }
+
+  // 生成红色标记说明
+  private generateRedMarkExplanation(employeeName: string, leaveRecords: LeaveRecord[], projectCount: number): string {
+    const explanations: string[] = [];
+    
+    // 检查请假情况
+    const leaveDays = leaveRecords.filter(record => record.isLeave);
+    if (leaveDays.length > 0) {
+      leaveDays.forEach(leave => {
+        const date = new Date(leave.date);
+        const dateStr = `${date.getMonth() + 1}月${date.getDate()}日`;
+        explanations.push(`${dateStr}(${leave.reason || '请假'})`);
+      });
+    }
+    
+    // 检查项目繁忙情况
+    if (projectCount >= 3) {
+      const today = new Date();
+      const dateStr = `${today.getMonth() + 1}月${today.getDate()}日`;
+      explanations.push(`${dateStr}(${projectCount}个项目繁忙)`);
+    }
+    
+    if (explanations.length === 0) {
+      return '当前无红色标记时段';
+    }
+    
+    return `甘特图中红色时段说明:${explanations.map((exp, index) => `${index + 1}${exp}`).join(';')}`;
+  }
+
+  // 关闭员工详情面板
+  closeEmployeeDetailPanel(): void {
+    this.showEmployeeDetailPanel = false;
+    this.selectedEmployeeDetail = null;
+  }
+
+  // 获取请假类型显示文本
+  getLeaveTypeText(leaveType?: string): string {
+    const typeMap: Record<string, string> = {
+      'sick': '病假',
+      'personal': '事假',
+      'annual': '年假',
+      'other': '其他'
+    };
+    return typeMap[leaveType || ''] || '请假';
+  }
+
+  // 生成请假覆盖层数据
+  private generateLeaveOverlayData(categories: string[], xMin: number, xMax: number): any[] {
+    const DAY = 24 * 60 * 60 * 1000;
+    const overlayData: any[] = [];
+
+    categories.forEach((employeeName, yIndex) => {
+      // 获取该员工在时间范围内的请假记录
+      const employeeLeaves = this.leaveRecords.filter(record => {
+        if (record.employeeName !== employeeName || !record.isLeave) {
+          return false;
+        }
+        
+        const recordDate = new Date(record.date).getTime();
+        return recordDate >= xMin && recordDate <= xMax;
+      });
+
+      // 为每个请假日期创建覆盖层
+      employeeLeaves.forEach(leave => {
+        const leaveDate = new Date(leave.date);
+        const startOfDay = new Date(leaveDate.getFullYear(), leaveDate.getMonth(), leaveDate.getDate()).getTime();
+        const endOfDay = startOfDay + DAY - 1;
+
+        overlayData.push({
+          name: `${employeeName} - ${this.getLeaveTypeText(leave.leaveType)}`,
+          value: [yIndex, startOfDay, endOfDay, employeeName, leave.leaveType, leave.reason],
+          itemStyle: { 
+            color: 'rgba(239, 68, 68, 0.6)', // 半透明红色
+            borderColor: '#ef4444',
+            borderWidth: 1
+          }
+        });
+      });
+
+      // 检查项目繁忙情况,如果项目数>=3,也添加红色标记
+      const employeeProjects = this.filteredProjects.filter(p => p.designerName === employeeName);
+      if (employeeProjects.length >= 3) {
+        // 在当前日期添加繁忙标记
+        const today = new Date();
+        const startOfToday = new Date(today.getFullYear(), today.getMonth(), today.getDate()).getTime();
+        const endOfToday = startOfToday + DAY - 1;
+
+        if (startOfToday >= xMin && startOfToday <= xMax) {
+          overlayData.push({
+            name: `${employeeName} - 项目繁忙(${employeeProjects.length}个项目)`,
+            value: [yIndex, startOfToday, endOfToday, employeeName, 'busy', `负责${employeeProjects.length}个项目`],
+            itemStyle: { 
+              color: 'rgba(239, 68, 68, 0.4)', // 稍微透明的红色
+              borderColor: '#ef4444',
+              borderWidth: 1,
+              borderType: 'dashed' // 虚线边框区分请假和繁忙
+            }
+          });
+        }
+      }
+    });
+
+    return overlayData;
+  }
 }

+ 3 - 0
src/app/services/project.service.ts

@@ -68,6 +68,7 @@ export class ProjectService {
       highPriorityNeeds: ['需家具购买建议'],
       status: '进行中',
       currentStage: '建模',
+      stage: '建模',
       createdAt: new Date('2025-09-01'),
       deadline: new Date('2025-09-15'),
       assigneeId: 'designer1',
@@ -84,6 +85,7 @@ export class ProjectService {
       highPriorityNeeds: [],
       status: '进行中',
       currentStage: '渲染',
+      stage: '渲染',
       createdAt: new Date('2025-09-02'),
       deadline: new Date('2025-09-20'),
       assigneeId: 'designer1',
@@ -100,6 +102,7 @@ export class ProjectService {
       highPriorityNeeds: ['需快速交付'],
       status: '已完成',
       currentStage: '投诉处理',
+      stage: '投诉处理',
       createdAt: new Date('2025-08-20'),
       deadline: new Date('2025-09-05'),
       assigneeId: 'designer1',

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

@@ -0,0 +1,210 @@
+<div class="complaint-card">
+  <!-- 统计数据概览 -->
+  <div class="stats-overview">
+    <h4>投诉处理概览</h4>
+    <div class="stats-grid">
+      <div class="stat-item total">
+        <div class="stat-value">{{ stats().totalCount }}</div>
+        <div class="stat-label">总投诉数</div>
+      </div>
+      <div class="stat-item pending">
+        <div class="stat-value">{{ stats().pendingCount }}</div>
+        <div class="stat-label">待处理</div>
+      </div>
+      <div class="stat-item processing">
+        <div class="stat-value">{{ stats().processingCount }}</div>
+        <div class="stat-label">处理中</div>
+      </div>
+      <div class="stat-item resolved">
+        <div class="stat-value">{{ stats().resolvedCount }}</div>
+        <div class="stat-label">已解决</div>
+      </div>
+      <div class="stat-item high-priority">
+        <div class="stat-value">{{ stats().highPriorityCount }}</div>
+        <div class="stat-label">高优先级</div>
+      </div>
+      <div class="stat-item resolution-time">
+        <div class="stat-value">{{ stats().averageResolutionTime }}<span class="time-suffix">天</span></div>
+        <div class="stat-label">平均解决时间</div>
+      </div>
+    </div>
+  </div>
+
+  <!-- 优先级统计 -->
+  <div class="priority-stats">
+    <h5>优先级分布</h5>
+    <div class="priority-grid">
+      @for (priority of priorities; track priority.value) {
+        <div class="priority-item" [style.border-left-color]="priority.color">
+          <span class="priority-label">{{ priority.label }}</span>
+          <span class="priority-count">{{ stats().priorityStats[priority.value] || 0 }}</span>
+        </div>
+      }
+    </div>
+  </div>
+
+  <!-- 类型统计 -->
+  <div class="type-stats">
+    <h5>问题类型统计</h5>
+    <div class="type-grid">
+      @for (type of complaintTypes; track type.value) {
+        <div class="type-item">
+          <span class="type-label">{{ type.label }}</span>
+          <span class="type-count">{{ stats().typeStats[type.value] || 0 }}</span>
+        </div>
+      }
+    </div>
+  </div>
+
+  <!-- 筛选区域 -->
+  <div class="filter-section">
+    <div class="search-row">
+      <div class="search-group">
+        <label>搜索:</label>
+        <input 
+          type="text" 
+          class="search-input"
+          placeholder="搜索投诉内容、客户姓名..."
+          [value]="searchKeyword()"
+          (input)="updateSearchKeyword($event.target.value)">
+      </div>
+    </div>
+
+    <div class="filter-row">
+      <div class="filter-group">
+        <label>状态筛选:</label>
+        <div class="filter-buttons">
+          <button 
+            class="filter-btn"
+            [class.active]="statusFilter() === 'all'"
+            (click)="updateStatusFilter('all')">
+            全部
+          </button>
+          <button 
+            class="filter-btn pending"
+            [class.active]="statusFilter() === '待处理'"
+            (click)="updateStatusFilter('待处理')">
+            待处理
+          </button>
+          <button 
+            class="filter-btn processing"
+            [class.active]="statusFilter() === '处理中'"
+            (click)="updateStatusFilter('处理中')">
+            处理中
+          </button>
+          <button 
+            class="filter-btn resolved"
+            [class.active]="statusFilter() === '已解决'"
+            (click)="updateStatusFilter('已解决')">
+            已解决
+          </button>
+        </div>
+      </div>
+    </div>
+    
+    <div class="filter-row">
+      <div class="filter-group">
+        <label>优先级筛选:</label>
+        <select 
+          class="filter-select"
+          [value]="priorityFilter()"
+          (change)="updatePriorityFilter($event)">
+          <option value="all">全部优先级</option>
+          @for (priority of priorities; track priority.value) {
+            <option [value]="priority.value">{{ priority.label }}</option>
+          }
+        </select>
+      </div>
+      
+      <div class="filter-group">
+        <label>类型筛选:</label>
+        <select 
+          class="filter-select"
+          [value]="typeFilter()"
+          (change)="updateTypeFilter($event)">
+          <option value="all">全部类型</option>
+          @for (type of complaintTypes; track type.value) {
+            <option [value]="type.value">{{ type.label }}</option>
+          }
+        </select>
+      </div>
+    </div>
+  </div>
+
+  <!-- 投诉列表 -->
+  <div class="complaints-list">
+    @if (filteredComplaints() && filteredComplaints().length > 0) {
+      <div class="list-body">
+        @for (complaint of filteredComplaints(); track complaint.id) {
+          <div class="complaint-item" [class]="getStatusClass(complaint)" [class.overdue]="isOverdue(complaint)">
+            <div class="complaint-header">
+              <div class="complaint-info">
+                <div class="complaint-title">
+                  <span class="type-tag">{{ getTypeLabel(getComplaintType(complaint)) }}</span>
+                  @if (complaint.customerName) {
+                    <span class="customer-name">{{ complaint.customerName }}</span>
+                  }
+                </div>
+                <div class="priority-badge" [class]="getPriorityClass(complaint.priority || 'low')" [style.background-color]="getPriorityInfo(complaint.priority || 'low').color">
+                  {{ getPriorityInfo(complaint.priority || 'low').label }}优先级
+                </div>
+              </div>
+              <div class="complaint-meta">
+                <span class="status-badge" [class]="getStatusClass(complaint)">
+                  {{ complaint.status }}
+                </span>
+                @if (isOverdue(complaint)) {
+                  <span class="overdue-badge">超时</span>
+                }
+              </div>
+            </div>
+            
+            <div class="complaint-content">
+              <p class="description">{{ complaint.description }}</p>
+              @if (complaint.images && complaint.images.length > 0) {
+                <div class="complaint-images">
+                  @for (image of complaint.images; track $index) {
+                    <img [src]="image" [alt]="'投诉图片' + ($index + 1)" class="complaint-image">
+                  }
+                </div>
+              }
+            </div>
+            
+            <div class="complaint-footer">
+              <div class="time-info">
+                <span class="submitted-time">提交时间:{{ complaint.submittedAt | date:'yyyy-MM-dd HH:mm' }}</span>
+                <span class="days-progress">已处理 {{ getDaysInProgress(complaint) }} 天</span>
+                @if (complaint.resolvedAt) {
+                  <span class="resolved-time">解决时间:{{ complaint.resolvedAt | date:'yyyy-MM-dd HH:mm' }}</span>
+                }
+              </div>
+              
+              @if (complaint.handlerComment) {
+                <div class="handler-response">
+                  <strong>处理意见:</strong>
+                  <p class="response-text">{{ complaint.handlerComment }}</p>
+                  @if (complaint.handlerName) {
+                    <span class="handler-name">处理人:{{ complaint.handlerName }}</span>
+                  }
+                </div>
+              }
+              
+              @if (complaint.solution) {
+                <div class="solution-section">
+                  <strong>解决方案:</strong>
+                  <p class="solution-text">{{ complaint.solution }}</p>
+                </div>
+              }
+            </div>
+          </div>
+        }
+      </div>
+    } @else {
+      <div class="empty-state">
+        <div class="empty-icon">📋</div>
+        <div class="empty-title">暂无投诉记录</div>
+        <div class="empty-desc">当前筛选条件下没有找到相关的投诉记录</div>
+      </div>
+    }
+  </div>
+</div>

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

@@ -0,0 +1,530 @@
+@use '../../styles/_ios-theme.scss' as *;
+@import '../../../../styles/variables';
+
+:host { display: block; height: 100%; }
+
+.complaint-card {
+  padding: $ios-spacing-md;
+  background: $ios-background;
+  border-radius: 12px;
+  border: 1px solid $ios-border;
+
+  // 统计数据概览
+  .stats-overview {
+    margin-bottom: $ios-spacing-lg;
+    
+    h4 {
+      margin: 0 0 $ios-spacing-md 0;
+      font-size: $ios-font-size-lg;
+      font-weight: $ios-font-weight-semibold;
+      color: $ios-text-primary;
+    }
+
+    .stats-grid {
+      display: grid;
+      grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
+      gap: $ios-spacing-md;
+
+      .stat-item {
+        text-align: center;
+        padding: $ios-spacing-md;
+        background: $ios-background-secondary;
+        border-radius: $ios-border-radius-md;
+        border: 1px solid $ios-border;
+
+        .stat-value {
+          font-size: $ios-font-size-xl;
+          font-weight: $ios-font-weight-bold;
+          color: $ios-text-primary;
+          margin-bottom: $ios-spacing-xs;
+
+          .time-suffix {
+            font-size: $ios-font-size-sm;
+            color: $ios-text-secondary;
+          }
+        }
+
+        .stat-label {
+          font-size: $ios-font-size-xs;
+          color: $ios-text-secondary;
+        }
+
+        &.total .stat-value { color: $ios-primary; }
+        &.pending .stat-value { color: $ios-warning; }
+        &.processing .stat-value { color: #1890ff; }
+        &.resolved .stat-value { color: $ios-success; }
+        &.high-priority .stat-value { color: $ios-danger; }
+        &.resolution-time .stat-value { color: #722ed1; }
+      }
+    }
+  }
+
+  // 优先级统计
+  .priority-stats {
+    margin-bottom: $ios-spacing-lg;
+    padding: $ios-spacing-md;
+    background: $ios-background-secondary;
+    border-radius: $ios-border-radius-md;
+    border: 1px solid $ios-border;
+
+    h5 {
+      margin: 0 0 $ios-spacing-md 0;
+      font-size: $ios-font-size-md;
+      font-weight: $ios-font-weight-semibold;
+      color: $ios-text-primary;
+    }
+
+    .priority-grid {
+      display: grid;
+      grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
+      gap: $ios-spacing-sm;
+
+      .priority-item {
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+        padding: $ios-spacing-sm;
+        background: $ios-background;
+        border-radius: $ios-border-radius-sm;
+        border: 1px solid $ios-border;
+        border-left-width: 4px;
+
+        .priority-label {
+          font-size: $ios-font-size-sm;
+          color: $ios-text-secondary;
+        }
+
+        .priority-count {
+          font-size: $ios-font-size-md;
+          font-weight: $ios-font-weight-semibold;
+          color: $ios-text-primary;
+        }
+      }
+    }
+  }
+
+  // 类型统计
+  .type-stats {
+    margin-bottom: $ios-spacing-lg;
+    padding: $ios-spacing-md;
+    background: $ios-background-secondary;
+    border-radius: $ios-border-radius-md;
+    border: 1px solid $ios-border;
+
+    h5 {
+      margin: 0 0 $ios-spacing-md 0;
+      font-size: $ios-font-size-md;
+      font-weight: $ios-font-weight-semibold;
+      color: $ios-text-primary;
+    }
+
+    .type-grid {
+      display: grid;
+      grid-template-columns: repeat(auto-fit, minmax(100px, 1fr));
+      gap: $ios-spacing-sm;
+
+      .type-item {
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+        padding: $ios-spacing-sm;
+        background: $ios-background;
+        border-radius: $ios-border-radius-sm;
+        border: 1px solid $ios-border;
+
+        .type-label {
+          font-size: $ios-font-size-xs;
+          color: $ios-text-secondary;
+        }
+
+        .type-count {
+          font-size: $ios-font-size-sm;
+          font-weight: $ios-font-weight-semibold;
+          color: $ios-primary;
+        }
+      }
+    }
+  }
+
+  // 筛选区域
+  .filter-section {
+    margin-bottom: $ios-spacing-lg;
+    padding: $ios-spacing-md;
+    background: $ios-background-secondary;
+    border-radius: $ios-border-radius-md;
+    border: 1px solid $ios-border;
+
+    .search-row {
+      margin-bottom: $ios-spacing-md;
+
+      .search-group {
+        display: flex;
+        align-items: center;
+        gap: $ios-spacing-sm;
+
+        label {
+          font-size: $ios-font-size-sm;
+          color: $ios-text-secondary;
+          white-space: nowrap;
+        }
+
+        .search-input {
+          flex: 1;
+          padding: $ios-spacing-sm;
+          border: 1px solid $ios-border;
+          border-radius: $ios-border-radius-sm;
+          background: $ios-background;
+          color: $ios-text-primary;
+          font-size: $ios-font-size-sm;
+
+          &:focus {
+            outline: none;
+            border-color: $ios-primary;
+          }
+
+          &::placeholder {
+            color: $ios-text-secondary;
+          }
+        }
+      }
+    }
+
+    .filter-row {
+      display: flex;
+      flex-wrap: wrap;
+      gap: $ios-spacing-md;
+      margin-bottom: $ios-spacing-md;
+
+      &:last-child {
+        margin-bottom: 0;
+      }
+
+      .filter-group {
+        display: flex;
+        align-items: center;
+        gap: $ios-spacing-sm;
+
+        label {
+          font-size: $ios-font-size-sm;
+          color: $ios-text-secondary;
+          white-space: nowrap;
+        }
+
+        .filter-buttons {
+          display: flex;
+          gap: $ios-spacing-xs;
+
+          .filter-btn {
+            padding: $ios-spacing-xs $ios-spacing-sm;
+            border: 1px solid $ios-border;
+            background: $ios-background;
+            color: $ios-text-secondary;
+            border-radius: $ios-border-radius-sm;
+            font-size: $ios-font-size-xs;
+            cursor: pointer;
+            transition: all 0.2s ease;
+
+            &:hover {
+              background: $ios-background-secondary;
+            }
+
+            &.active {
+              background: $ios-primary;
+              color: white;
+              border-color: $ios-primary;
+            }
+
+            &.pending.active {
+              background: $ios-warning;
+              border-color: $ios-warning;
+            }
+
+            &.processing.active {
+              background: #1890ff;
+              border-color: #1890ff;
+            }
+
+            &.resolved.active {
+              background: $ios-success;
+              border-color: $ios-success;
+            }
+          }
+        }
+
+        .filter-select {
+          padding: $ios-spacing-xs $ios-spacing-sm;
+          border: 1px solid $ios-border;
+          border-radius: $ios-border-radius-sm;
+          background: $ios-background;
+          color: $ios-text-primary;
+          font-size: $ios-font-size-xs;
+          min-width: 120px;
+
+          &:focus {
+            outline: none;
+            border-color: $ios-primary;
+          }
+        }
+      }
+    }
+  }
+
+  // 投诉列表
+  .complaints-list {
+    .list-body {
+      .complaint-item {
+        padding: $ios-spacing-md;
+        margin-bottom: $ios-spacing-md;
+        background: $ios-background;
+        border: 1px solid $ios-border;
+        border-radius: $ios-border-radius-md;
+        transition: all 0.2s ease;
+
+        &:hover {
+          box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+        }
+
+        &:last-child {
+          margin-bottom: 0;
+        }
+
+        &.overdue {
+          border-left: 4px solid $ios-danger;
+          background: rgba($ios-danger, 0.02);
+        }
+
+        .complaint-header {
+          display: flex;
+          justify-content: space-between;
+          align-items: flex-start;
+          margin-bottom: $ios-spacing-md;
+
+          .complaint-info {
+            flex: 1;
+
+            .complaint-title {
+              display: flex;
+              align-items: center;
+              gap: $ios-spacing-sm;
+              margin-bottom: $ios-spacing-xs;
+
+              .type-tag {
+                padding: $ios-spacing-xs $ios-spacing-sm;
+                background: $ios-primary;
+                color: white;
+                border-radius: $ios-border-radius-sm;
+                font-size: $ios-font-size-xs;
+              }
+
+              .customer-name {
+                font-size: $ios-font-size-md;
+                font-weight: $ios-font-weight-semibold;
+                color: $ios-text-primary;
+              }
+            }
+
+            .priority-badge {
+              display: inline-block;
+              padding: $ios-spacing-xs $ios-spacing-sm;
+              color: white;
+              border-radius: $ios-border-radius-sm;
+              font-size: $ios-font-size-xs;
+              font-weight: $ios-font-weight-semibold;
+            }
+          }
+
+          .complaint-meta {
+            display: flex;
+            align-items: center;
+            gap: $ios-spacing-sm;
+
+            .status-badge {
+              padding: $ios-spacing-xs $ios-spacing-sm;
+              border-radius: $ios-border-radius-sm;
+              font-size: $ios-font-size-xs;
+              font-weight: $ios-font-weight-semibold;
+
+              &.pending {
+                background: rgba($ios-warning, 0.1);
+                color: $ios-warning;
+              }
+
+              &.processing {
+                background: rgba(#1890ff, 0.1);
+                color: #1890ff;
+              }
+
+              &.resolved {
+                background: rgba($ios-success, 0.1);
+                color: $ios-success;
+              }
+            }
+
+            .overdue-badge {
+              padding: $ios-spacing-xs $ios-spacing-sm;
+              background: rgba($ios-danger, 0.1);
+              color: $ios-danger;
+              border-radius: $ios-border-radius-sm;
+              font-size: $ios-font-size-xs;
+              font-weight: $ios-font-weight-semibold;
+            }
+          }
+        }
+
+        .complaint-content {
+          margin-bottom: $ios-spacing-md;
+
+          .description {
+            margin: 0 0 $ios-spacing-sm 0;
+            color: $ios-text-primary;
+            line-height: 1.5;
+          }
+
+          .complaint-images {
+            display: flex;
+            gap: $ios-spacing-sm;
+            flex-wrap: wrap;
+
+            .complaint-image {
+              width: 80px;
+              height: 80px;
+              object-fit: cover;
+              border-radius: $ios-border-radius-sm;
+              border: 1px solid $ios-border;
+            }
+          }
+        }
+
+        .complaint-footer {
+          .time-info {
+            display: flex;
+            flex-wrap: wrap;
+            gap: $ios-spacing-md;
+            margin-bottom: $ios-spacing-sm;
+
+            .submitted-time,
+            .days-progress,
+            .resolved-time {
+              font-size: $ios-font-size-xs;
+              color: $ios-text-secondary;
+            }
+
+            .days-progress {
+              color: $ios-primary;
+              font-weight: $ios-font-weight-semibold;
+            }
+
+            .resolved-time {
+              color: $ios-success;
+            }
+          }
+
+          .handler-response {
+            padding: $ios-spacing-sm;
+            background: rgba($ios-primary, 0.05);
+            border-radius: $ios-border-radius-sm;
+            border-left: 3px solid $ios-primary;
+            margin-bottom: $ios-spacing-sm;
+
+            strong {
+              color: $ios-primary;
+              font-size: $ios-font-size-sm;
+            }
+
+            .response-text {
+              margin: $ios-spacing-xs 0;
+              color: $ios-text-secondary;
+              font-size: $ios-font-size-sm;
+              line-height: 1.4;
+            }
+
+            .handler-name {
+              font-size: $ios-font-size-xs;
+              color: $ios-text-secondary;
+            }
+          }
+
+          .solution-section {
+            padding: $ios-spacing-sm;
+            background: rgba($ios-success, 0.05);
+            border-radius: $ios-border-radius-sm;
+            border-left: 3px solid $ios-success;
+
+            strong {
+              color: $ios-success;
+              font-size: $ios-font-size-sm;
+            }
+
+            .solution-text {
+              margin: $ios-spacing-xs 0 0 0;
+              color: $ios-text-secondary;
+              font-size: $ios-font-size-sm;
+              line-height: 1.4;
+            }
+          }
+        }
+      }
+    }
+
+    .empty-state {
+      text-align: center;
+      padding: $ios-spacing-xl;
+      color: $ios-text-secondary;
+
+      .empty-icon {
+        font-size: 48px;
+        margin-bottom: $ios-spacing-md;
+      }
+
+      .empty-title {
+        font-size: $ios-font-size-lg;
+        font-weight: $ios-font-weight-semibold;
+        color: $ios-text-primary;
+        margin-bottom: $ios-spacing-sm;
+      }
+
+      .empty-desc {
+        font-size: $ios-font-size-sm;
+        color: $ios-text-secondary;
+      }
+    }
+  }
+
+  // 响应式设计
+  @media (max-width: 768px) {
+    .stats-overview .stats-grid {
+      grid-template-columns: repeat(2, 1fr);
+    }
+
+    .priority-stats .priority-grid,
+    .type-stats .type-grid {
+      grid-template-columns: 1fr;
+    }
+
+    .filter-section {
+      .search-row .search-group {
+        flex-direction: column;
+        align-items: stretch;
+      }
+
+      .filter-row {
+        flex-direction: column;
+        align-items: stretch;
+
+        .filter-group {
+          flex-direction: column;
+          align-items: stretch;
+
+          .filter-buttons {
+            justify-content: center;
+          }
+        }
+      }
+    }
+
+    .complaints-list .complaint-item .complaint-header {
+      flex-direction: column;
+      gap: $ios-spacing-sm;
+    }
+  }
+}

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

@@ -0,0 +1,224 @@
+import { Component, Input, computed, signal } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FormsModule } from '@angular/forms';
+
+export interface ComplaintItem {
+  id: string;
+  type: string;
+  description: string;
+  submitTime: any;
+  submittedAt?: Date;
+  resolvedAt?: Date;
+  status: string;
+  response?: string;
+  handlerComment?: string;
+  handlerName?: string;
+  solution?: string;
+  customerName?: string;
+  priority?: string;
+  images?: string[];
+}
+
+interface ComplaintStats {
+  totalCount: number;
+  pendingCount: number;
+  processingCount: number;
+  resolvedCount: number;
+  highPriorityCount: number;
+  averageResolutionTime: number;
+  priorityStats: { [key: string]: number };
+  typeStats: { [key: string]: number };
+}
+
+@Component({
+  selector: 'app-complaint-card',
+  standalone: true,
+  imports: [CommonModule, FormsModule],
+  templateUrl: './complaint-card.html',
+  styleUrls: ['./complaint-card.scss']
+})
+export class ComplaintCardComponent {
+  @Input() complaints: ComplaintItem[] = [];
+
+  // 筛选条件
+  statusFilter = signal<string>('all');
+  priorityFilter = signal<string>('all');
+  typeFilter = signal<string>('all');
+  searchKeyword = signal<string>('');
+
+  // 投诉类型
+  complaintTypes = [
+    { value: 'quality', label: '质量问题' },
+    { value: 'service', label: '服务态度' },
+    { value: 'delivery', label: '交付延期' },
+    { value: 'communication', label: '沟通问题' },
+    { value: 'design', label: '设计不满意' },
+    { value: 'other', label: '其他问题' }
+  ];
+
+  // 优先级选项
+  priorities = [
+    { value: 'low', label: '低', color: '#52c41a' },
+    { value: 'medium', label: '中', color: '#faad14' },
+    { value: 'high', label: '高', color: '#f5222d' },
+    { value: 'urgent', label: '紧急', color: '#722ed1' }
+  ];
+
+  // 计算统计数据
+  stats = computed<ComplaintStats>(() => {
+    const complaints = this.complaints || [];
+    const totalCount = complaints.length;
+    
+    const pendingCount = complaints.filter(c => c.status === '待处理').length;
+    const processingCount = complaints.filter(c => c.status === '处理中').length;
+    const resolvedCount = complaints.filter(c => c.status === '已解决').length;
+    const highPriorityCount = complaints.filter(c => (c as any).priority === 'high' || (c as any).priority === 'urgent').length;
+
+    // 计算平均解决时间(天)
+    const resolvedComplaints = complaints.filter(c => c.status === '已解决' && (c as any).resolvedAt && (c as any).createdAt);
+    const averageResolutionTime = resolvedComplaints.length > 0 
+      ? resolvedComplaints.reduce((sum, c) => {
+          const days = Math.ceil((new Date((c as any).resolvedAt).getTime() - new Date((c as any).createdAt).getTime()) / (1000 * 60 * 60 * 24));
+          return sum + days;
+        }, 0) / resolvedComplaints.length
+      : 0;
+
+    // 优先级统计
+    const priorityStats: { [key: string]: number } = {};
+    this.priorities.forEach(p => {
+      priorityStats[p.value] = complaints.filter(c => (c as any).priority === p.value).length;
+    });
+
+    // 类型统计
+    const typeStats: { [key: string]: number } = {};
+    this.complaintTypes.forEach(t => {
+      typeStats[t.value] = complaints.filter(c => this.getComplaintType(c) === t.value).length;
+    });
+
+    return {
+      totalCount,
+      pendingCount,
+      processingCount,
+      resolvedCount,
+      highPriorityCount,
+      averageResolutionTime: Math.round(averageResolutionTime * 10) / 10,
+      priorityStats,
+      typeStats
+    };
+  });
+
+  // 筛选后的投诉列表
+  filteredComplaints = computed(() => {
+    let filtered = this.complaints || [];
+
+    // 状态筛选
+    if (this.statusFilter() !== 'all') {
+      filtered = filtered.filter(complaint => complaint.status === this.statusFilter());
+    }
+
+    // 优先级筛选
+    if (this.priorityFilter() !== 'all') {
+      filtered = filtered.filter(complaint => (complaint as any).priority === this.priorityFilter());
+    }
+
+    // 类型筛选
+    if (this.typeFilter() !== 'all') {
+      filtered = filtered.filter(complaint => this.getComplaintType(complaint) === this.typeFilter());
+    }
+
+    // 关键词搜索
+    if (this.searchKeyword()) {
+      const keyword = this.searchKeyword().toLowerCase();
+      filtered = filtered.filter(complaint => 
+        complaint.description?.toLowerCase().includes(keyword) ||
+        (complaint as any).customerName?.toLowerCase().includes(keyword) ||
+        complaint.type?.toLowerCase().includes(keyword)
+      );
+    }
+
+    return filtered;
+  });
+
+  // 获取投诉类型
+  getComplaintType(complaint: any): string {
+    if (complaint.type) return complaint.type;
+    
+    // 根据描述内容推断类型
+    const description = complaint.description?.toLowerCase() || '';
+    if (description.includes('质量') || description.includes('缺陷')) return 'quality';
+    if (description.includes('服务') || description.includes('态度')) return 'service';
+    if (description.includes('延期') || description.includes('时间')) return 'delivery';
+    if (description.includes('沟通') || description.includes('联系')) return 'communication';
+    if (description.includes('设计') || description.includes('效果')) return 'design';
+    
+    return 'other';
+  }
+
+  // 获取类型标签
+  getTypeLabel(type: string): string {
+    const typeObj = this.complaintTypes.find(t => t.value === type);
+    return typeObj ? typeObj.label : '其他';
+  }
+
+  // 获取优先级信息
+  getPriorityInfo(priority: string) {
+    return this.priorities.find(p => p.value === priority) || this.priorities[0];
+  }
+
+  // 获取状态样式类
+  getStatusClass(complaint: any): string {
+    switch (complaint.status) {
+      case '待处理': return 'pending';
+      case '处理中': return 'processing';
+      case '已解决': return 'resolved';
+      default: return 'pending';
+    }
+  }
+
+  // 获取优先级样式类
+  getPriorityClass(priority: string): string {
+    switch (priority) {
+      case 'low': return 'priority-low';
+      case 'medium': return 'priority-medium';
+      case 'high': return 'priority-high';
+      case 'urgent': return 'priority-urgent';
+      default: return 'priority-low';
+    }
+  }
+
+  // 计算处理天数
+  getDaysInProgress(complaint: any): number {
+    const startDate = new Date(complaint.createdAt);
+    const endDate = complaint.resolvedAt ? new Date(complaint.resolvedAt) : new Date();
+    return Math.ceil((endDate.getTime() - startDate.getTime()) / (1000 * 60 * 60 * 24));
+  }
+
+  // 判断是否超时
+  isOverdue(complaint: any): boolean {
+    if (complaint.status === '已解决') return false;
+    
+    const daysInProgress = this.getDaysInProgress(complaint);
+    const maxDays = complaint.priority === 'urgent' ? 1 : 
+                   complaint.priority === 'high' ? 3 : 
+                   complaint.priority === 'medium' ? 7 : 14;
+    
+    return daysInProgress > maxDays;
+  }
+
+  // 更新筛选条件
+  updateStatusFilter(event: any) {
+    this.statusFilter.set(event.target.value);
+  }
+
+  updatePriorityFilter(event: any) {
+    this.priorityFilter.set(event.target.value);
+  }
+
+  updateTypeFilter(event: any) {
+    this.typeFilter.set(event.target.value);
+  }
+
+  updateSearchKeyword(keyword: string) {
+    this.searchKeyword.set(keyword);
+  }
+}

+ 249 - 0
src/app/shared/components/consultation-order-panel/consultation-order-panel.component.html

@@ -0,0 +1,249 @@
+<div class="consultation-order-panel">
+  <!-- 面板头部 -->
+  <div class="panel-header">
+    <h1 class="panel-title">创建咨询订单</h1>
+    <p class="panel-subtitle">填写客户信息和项目需求</p>
+  </div>
+
+  <!-- 可滚动内容区域 -->
+  <div class="panel-content">
+    <!-- 客户信息栏 -->
+    <section class="customer-info-section">
+      <h2 class="section-title">客户信息</h2>
+      
+      @if (!selectedCustomer) {
+        <!-- 客户搜索 -->
+        <div class="search-container">
+          <input 
+            type="text" 
+            [(ngModel)]="searchKeyword" 
+            (ngModelChange)="searchCustomer()"
+            placeholder="输入客户姓名或手机号搜索..."
+            class="search-input"
+            autocomplete="off"
+          />
+          <button class="search-btn" (click)="searchCustomer()">搜索</button>
+        </div>
+        
+        <!-- 搜索结果 -->
+        @if (searchResults.length > 0) {
+          <div class="search-results">
+            @for (customer of searchResults; track customer.id) {
+              <div class="result-item" (click)="selectCustomer(customer)">
+                <div class="customer-name">{{ customer.name }}</div>
+                <div class="customer-info">{{ customer.phone }}</div>
+              </div>
+            }
+          </div>
+        }
+      } @else {
+        <!-- 已选择客户 -->
+        <div class="selected-customer">
+          <div class="customer-details">
+            <div class="customer-name">{{ selectedCustomer.name }}</div>
+            <div class="customer-phone">{{ selectedCustomer.phone }}</div>
+          </div>
+          <button class="clear-btn" (click)="clearSelectedCustomer()">重新选择</button>
+        </div>
+      }
+    </section>
+
+    <!-- 新客户表单 -->
+    @if (!selectedCustomer) {
+      <section class="new-customer-form">
+        <h3 class="form-title">新客户信息</h3>
+        <form [formGroup]="customerForm">
+          <div class="form-row">
+            <div class="form-group">
+              <label for="name">客户姓名 <span class="required">*</span></label>
+              <input type="text" id="name" formControlName="name" placeholder="请输入客户姓名">
+            </div>
+            <div class="form-group">
+              <label for="phone">手机号码 <span class="required">*</span></label>
+              <input type="tel" id="phone" formControlName="phone" placeholder="请输入手机号码">
+            </div>
+          </div>
+          <div class="form-row">
+            <div class="form-group">
+              <label for="wechat">微信号</label>
+              <input type="text" id="wechat" formControlName="wechat" placeholder="请输入微信号(选填)">
+            </div>
+            <div class="form-group">
+              <label for="customerType">客户类型</label>
+              <select id="customerType" formControlName="customerType">
+                <option value="">请选择客户类型</option>
+                <option value="新客户">新客户</option>
+                <option value="老客户">老客户</option>
+                <option value="转介绍">转介绍</option>
+              </select>
+            </div>
+          </div>
+        </form>
+      </section>
+    }
+
+    <!-- 项目需求卡片 -->
+    <section class="requirements-card">
+      <div class="card-header" (click)="isRequirementCardExpanded = !isRequirementCardExpanded">
+        <h3 class="card-title">项目需求</h3>
+        <span class="toggle-icon" [class.expanded]="isRequirementCardExpanded">▼</span>
+      </div>
+      
+      @if (isRequirementCardExpanded) {
+        <div class="card-content">
+          <form [formGroup]="requirementForm">
+            <!-- 基本信息 -->
+            <div class="form-row">
+              <div class="form-group">
+                <label for="decorationType">装修类型 <span class="required">*</span></label>
+                <select id="decorationType" formControlName="decorationType">
+                  <option value="">请选择装修类型</option>
+                  <option value="全包">全包</option>
+                  <option value="半包">半包</option>
+                  <option value="清包">清包</option>
+                  <option value="软装">软装</option>
+                </select>
+              </div>
+              <div class="form-group">
+                <label for="downPayment">定金金额 <span class="required">*</span></label>
+                <input type="number" id="downPayment" formControlName="downPayment" placeholder="请输入定金金额">
+              </div>
+            </div>
+
+            <div class="form-row">
+              <div class="form-group">
+                <label for="firstDraftDate">初稿时间 <span class="required">*</span></label>
+                <input type="date" id="firstDraftDate" formControlName="firstDraftDate">
+              </div>
+              <div class="form-group">
+                <label for="budget">预算范围 <span class="required">*</span></label>
+                <input type="text" id="budget" formControlName="budget" placeholder="请输入预算范围">
+              </div>
+            </div>
+
+            <div class="form-row">
+              <div class="form-group">
+                <label for="area">房屋面积 <span class="required">*</span></label>
+                <input type="number" id="area" formControlName="area" placeholder="请输入房屋面积(平方米)">
+              </div>
+              <div class="form-group">
+                <label for="houseType">房屋类型</label>
+                <input type="text" id="houseType" formControlName="houseType" placeholder="如:三室两厅">
+              </div>
+            </div>
+
+            <div class="form-row">
+              <div class="form-group">
+                <label for="style">装修风格</label>
+                <select id="style" formControlName="style">
+                  <option value="">请选择装修风格</option>
+                  @for (style of styleOptions; track style) {
+                    <option [value]="style">{{ style }}</option>
+                  }
+                </select>
+              </div>
+            </div>
+
+            <!-- 详细需求 -->
+            <div class="form-row">
+              <div class="form-group">
+                <label for="spaceRequirements">涉及空间</label>
+                <textarea 
+                  id="spaceRequirements" 
+                  formControlName="spaceRequirements" 
+                  rows="3" 
+                  placeholder="请描述涉及的空间,如:客厅、卧室、厨房等"
+                ></textarea>
+              </div>
+            </div>
+
+            <div class="form-row">
+              <div class="form-group">
+                <label for="designAngles">设计角度</label>
+                <textarea 
+                  id="designAngles" 
+                  formControlName="designAngles" 
+                  rows="3" 
+                  placeholder="请明确各个空间的展示角度"
+                ></textarea>
+              </div>
+            </div>
+
+            <div class="form-row">
+              <div class="form-group">
+                <label for="specialAreaHandling">特殊区域处理</label>
+                <textarea 
+                  id="specialAreaHandling" 
+                  formControlName="specialAreaHandling" 
+                  rows="3" 
+                  placeholder="请描述特殊区域的处理要求"
+                ></textarea>
+              </div>
+            </div>
+
+            <div class="form-row">
+              <div class="form-group">
+                <label for="materialRequirements">材质要求</label>
+                <textarea 
+                  id="materialRequirements" 
+                  formControlName="materialRequirements" 
+                  rows="3" 
+                  placeholder="请描述具体的材质要求"
+                ></textarea>
+              </div>
+            </div>
+
+            <div class="form-row">
+              <div class="form-group">
+                <label for="lightingRequirements">灯光要求</label>
+                <textarea 
+                  id="lightingRequirements" 
+                  formControlName="lightingRequirements" 
+                  rows="3" 
+                  placeholder="请描述灯光设计要求"
+                ></textarea>
+              </div>
+            </div>
+
+            <!-- 偏好标签 -->
+            <div class="tags-section">
+              <h4 class="tags-title">偏好标签</h4>
+              <div class="tags-container">
+                @for (tag of preferenceTags; track tag) {
+                  <span class="tag">
+                    {{ tag }}
+                    <button type="button" class="remove-tag" (click)="removePreferenceTag(tag)">×</button>
+                  </span>
+                }
+              </div>
+              <div class="tag-input-container">
+                <input 
+                  type="text" 
+                  [(ngModel)]="newTag" 
+                  placeholder="添加偏好标签"
+                  class="tag-input"
+                  (keyup.enter)="addPreferenceTag(newTag)"
+                />
+                <button type="button" class="add-tag-btn" (click)="addPreferenceTag(newTag)">添加</button>
+              </div>
+            </div>
+          </form>
+        </div>
+      }
+    </section>
+  </div>
+
+  <!-- 底部提交区域 -->
+  <div class="panel-footer">
+    <button type="submit" 
+            class="submit-btn" 
+            [disabled]="!customerForm.valid || !requirementForm.valid || isSubmitting"
+            (click)="submitForm()">
+      @if (isSubmitting) {
+        <span>创建中...</span>
+      } @else {
+        <span>创建订单</span>
+      }
+    </button>
+  </div>
+</div>

+ 570 - 0
src/app/shared/components/consultation-order-panel/consultation-order-panel.component.scss

@@ -0,0 +1,570 @@
+// iOS风格变量定义
+$ios-primary: #007AFF;
+$ios-primary-dark: #0062CC;
+$ios-secondary: #34C759;
+$ios-success: #34C759;
+$ios-warning: #FF9500;
+$ios-danger: #FF3B30;
+$ios-text-primary: #000000;
+$ios-text-secondary: #3C3C43;
+$ios-text-tertiary: #8E8E93;
+$ios-border: #D1D1D6;
+$ios-background: #FFFFFF;
+$ios-background-secondary: #F2F2F7;
+$ios-background-tertiary: #E5E5EA;
+$ios-shadow-sm: 0 1px 3px rgba(0,0,0,0.1);
+$ios-shadow-md: 0 4px 10px rgba(0,0,0,0.1);
+$ios-shadow-lg: 0 10px 30px rgba(0,0,0,0.1);
+$ios-radius: 10px;
+$ios-radius-lg: 16px;
+$ios-transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
+$ios-font-family: -apple-system, BlinkMacSystemFont, 'PingFang SC', sans-serif;
+$ios-spacing-xs: 4px;
+$ios-spacing-sm: 8px;
+$ios-spacing-md: 16px;
+$ios-spacing-lg: 24px;
+$ios-spacing-xl: 32px;
+
+// 咨询订单面板样式 - 适配固定区域
+.consultation-order-panel {
+  background: $ios-background;
+  border-radius: $ios-radius-lg;
+  box-shadow: $ios-shadow-md;
+  height: 100%;
+  max-height: 600px;
+  display: flex;
+  flex-direction: column;
+  font-family: $ios-font-family;
+  border: 1px solid $ios-border;
+  overflow: hidden;
+
+  // 面板头部
+  .panel-header {
+    padding: $ios-spacing-md $ios-spacing-lg;
+    background: linear-gradient(135deg, $ios-background 0%, $ios-background-secondary 100%);
+    border-bottom: 1px solid $ios-border;
+    flex-shrink: 0;
+
+    .panel-title {
+      font-size: 18px;
+      font-weight: 600;
+      color: $ios-text-primary;
+      margin: 0;
+    }
+
+    .panel-subtitle {
+      font-size: 14px;
+      color: $ios-text-secondary;
+      margin: 4px 0 0 0;
+    }
+  }
+
+  // 可滚动内容区域
+  .panel-content {
+    flex: 1;
+    overflow-y: auto;
+    padding: $ios-spacing-md $ios-spacing-lg;
+    
+    // iOS风格滚动条
+    &::-webkit-scrollbar {
+      width: 6px;
+    }
+    
+    &::-webkit-scrollbar-track {
+      background: $ios-background-secondary;
+      border-radius: 3px;
+    }
+    
+    &::-webkit-scrollbar-thumb {
+      background: $ios-text-tertiary;
+      border-radius: 3px;
+      
+      &:hover {
+        background: $ios-text-secondary;
+      }
+    }
+  }
+
+  // 客户信息栏
+  .customer-info-section {
+    margin-bottom: $ios-spacing-lg;
+    padding: $ios-spacing-md;
+    background: $ios-background-secondary;
+    border-radius: $ios-radius;
+    border: 1px solid $ios-border;
+
+    .section-title {
+      font-size: 16px;
+      font-weight: 600;
+      color: $ios-text-primary;
+      margin-bottom: $ios-spacing-md;
+    }
+
+    .search-container {
+      display: flex;
+      align-items: center;
+      gap: $ios-spacing-sm;
+      margin-bottom: $ios-spacing-sm;
+
+      .search-input {
+        flex: 1;
+        padding: 12px $ios-spacing-md;
+        border: 2px solid $ios-border;
+        border-radius: $ios-radius;
+        font-size: 16px;
+        background: $ios-background;
+        transition: $ios-transition;
+
+        &:focus {
+          outline: none;
+          border-color: $ios-primary;
+          box-shadow: 0 0 0 4px rgba(0, 122, 255, 0.1);
+        }
+
+        &::placeholder {
+          color: $ios-text-tertiary;
+        }
+      }
+
+      .search-btn {
+        padding: 12px $ios-spacing-md;
+        background: $ios-primary;
+        color: $ios-background;
+        border: none;
+        border-radius: $ios-radius;
+        cursor: pointer;
+        font-size: 16px;
+        font-weight: 500;
+        transition: $ios-transition;
+
+        &:hover {
+          background: $ios-primary-dark;
+          transform: translateY(-1px);
+        }
+
+        &:active {
+          transform: translateY(0);
+        }
+      }
+    }
+
+    .search-results {
+      max-height: 200px;
+      overflow-y: auto;
+      border: 1px solid $ios-border;
+      border-radius: $ios-radius;
+      background: $ios-background;
+      margin-top: $ios-spacing-sm;
+
+      .result-item {
+        padding: $ios-spacing-md;
+        cursor: pointer;
+        border-bottom: 1px solid $ios-border;
+        transition: $ios-transition;
+
+        &:hover {
+          background: $ios-background-secondary;
+        }
+
+        &:last-child {
+          border-bottom: none;
+        }
+
+        .customer-name {
+          font-weight: 500;
+          color: $ios-text-primary;
+        }
+
+        .customer-info {
+          font-size: 14px;
+          color: $ios-text-secondary;
+          margin-top: 2px;
+        }
+      }
+    }
+
+    .selected-customer {
+      padding: $ios-spacing-md;
+      background: rgba($ios-primary, 0.1);
+      border-radius: $ios-radius;
+      border: 1px solid rgba($ios-primary, 0.2);
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      margin-top: $ios-spacing-sm;
+
+      .customer-details {
+        .customer-name {
+          font-weight: 600;
+          color: $ios-text-primary;
+        }
+
+        .customer-phone {
+          font-size: 14px;
+          color: $ios-text-secondary;
+          margin-top: 2px;
+        }
+      }
+
+      .clear-btn {
+        background: $ios-danger;
+        color: $ios-background;
+        border: none;
+        border-radius: 6px;
+        padding: 6px 12px;
+        cursor: pointer;
+        font-size: 14px;
+        font-weight: 500;
+        transition: $ios-transition;
+
+        &:hover {
+          background: darken($ios-danger, 10%);
+          transform: translateY(-1px);
+        }
+      }
+    }
+  }
+
+  // 新客户表单
+  .new-customer-form {
+    margin-bottom: $ios-spacing-lg;
+    padding: $ios-spacing-md;
+    background: $ios-background-secondary;
+    border-radius: $ios-radius;
+    border: 1px solid $ios-border;
+
+    .form-title {
+      font-size: 16px;
+      font-weight: 600;
+      margin-bottom: $ios-spacing-md;
+      color: $ios-text-primary;
+    }
+
+    .form-row {
+      display: flex;
+      gap: $ios-spacing-md;
+      margin-bottom: $ios-spacing-md;
+
+      @media (max-width: 768px) {
+        flex-direction: column;
+        gap: $ios-spacing-sm;
+      }
+
+      .form-group {
+        flex: 1;
+
+        label {
+          display: block;
+          margin-bottom: $ios-spacing-xs;
+          font-size: 14px;
+          font-weight: 500;
+          color: $ios-text-primary;
+        }
+
+        input, select, textarea {
+          width: 100%;
+          padding: 12px $ios-spacing-md;
+          border: 2px solid $ios-border;
+          border-radius: $ios-radius;
+          font-size: 16px;
+          background: $ios-background;
+          transition: $ios-transition;
+          box-sizing: border-box;
+
+          &:focus {
+            outline: none;
+            border-color: $ios-primary;
+            box-shadow: 0 0 0 4px rgba(0, 122, 255, 0.1);
+          }
+
+          &::placeholder {
+            color: $ios-text-tertiary;
+          }
+        }
+
+        textarea {
+          resize: vertical;
+          min-height: 80px;
+          font-family: $ios-font-family;
+        }
+
+        select {
+          cursor: pointer;
+        }
+      }
+    }
+  }
+
+  // 项目需求卡片
+  .requirements-card {
+    background: $ios-background;
+    border: 1px solid $ios-border;
+    border-radius: $ios-radius;
+    margin-bottom: $ios-spacing-md;
+    overflow: hidden;
+
+    .card-header {
+      padding: $ios-spacing-md;
+      background: linear-gradient(135deg, $ios-background-secondary 0%, $ios-background-tertiary 100%);
+      border-bottom: 1px solid $ios-border;
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      cursor: pointer;
+      transition: $ios-transition;
+
+      &:hover {
+        background: linear-gradient(135deg, $ios-background-tertiary 0%, $ios-border 100%);
+      }
+
+      .card-title {
+        font-size: 16px;
+        font-weight: 600;
+        color: $ios-text-primary;
+      }
+
+      .toggle-icon {
+        color: $ios-text-secondary;
+        transition: transform 0.3s ease;
+        font-size: 18px;
+
+        &.expanded {
+          transform: rotate(180deg);
+        }
+      }
+    }
+
+    .card-content {
+      padding: $ios-spacing-md;
+      max-height: 500px;
+      overflow-y: auto;
+
+      // iOS风格滚动条
+      &::-webkit-scrollbar {
+        width: 6px;
+      }
+      
+      &::-webkit-scrollbar-track {
+        background: $ios-background-secondary;
+        border-radius: 3px;
+      }
+      
+      &::-webkit-scrollbar-thumb {
+        background: $ios-text-tertiary;
+        border-radius: 3px;
+        
+        &:hover {
+          background: $ios-text-secondary;
+        }
+      }
+
+      .form-row {
+        display: flex;
+        gap: $ios-spacing-md;
+        margin-bottom: $ios-spacing-md;
+
+        @media (max-width: 768px) {
+          flex-direction: column;
+          gap: $ios-spacing-sm;
+        }
+
+        .form-group {
+          flex: 1;
+
+          label {
+            display: block;
+            margin-bottom: $ios-spacing-xs;
+            font-size: 14px;
+            font-weight: 500;
+            color: $ios-text-primary;
+          }
+
+          .required {
+            color: #ff4444;
+            font-weight: bold;
+          }
+
+          input, select, textarea {
+            width: 100%;
+            padding: 12px $ios-spacing-md;
+            border: 2px solid $ios-border;
+            border-radius: $ios-radius;
+            font-size: 16px;
+            background: $ios-background;
+            transition: $ios-transition;
+            box-sizing: border-box;
+
+            &:focus {
+              outline: none;
+              border-color: $ios-primary;
+              box-shadow: 0 0 0 4px rgba(0, 122, 255, 0.1);
+            }
+
+            &::placeholder {
+              color: $ios-text-tertiary;
+            }
+          }
+
+          select {
+            cursor: pointer;
+          }
+        }
+      }
+
+      .tags-section {
+        margin-top: $ios-spacing-md;
+
+        .tags-title {
+          font-size: 14px;
+          font-weight: 600;
+          margin-bottom: $ios-spacing-sm;
+          color: $ios-text-primary;
+        }
+
+        .tags-container {
+          display: flex;
+          flex-wrap: wrap;
+          gap: $ios-spacing-sm;
+          margin-bottom: $ios-spacing-md;
+
+          .tag {
+            padding: 6px 12px;
+            background: rgba($ios-primary, 0.1);
+            border: 1px solid rgba($ios-primary, 0.2);
+            border-radius: 20px;
+            font-size: 14px;
+            color: $ios-primary;
+            display: flex;
+            align-items: center;
+            gap: $ios-spacing-xs;
+            transition: $ios-transition;
+
+            &:hover {
+              background: rgba($ios-primary, 0.15);
+            }
+
+            .remove-tag {
+              background: none;
+              border: none;
+              color: $ios-primary;
+              cursor: pointer;
+              font-size: 16px;
+              padding: 0;
+              width: 18px;
+              height: 18px;
+              display: flex;
+              align-items: center;
+              justify-content: center;
+              border-radius: 50%;
+              transition: $ios-transition;
+
+              &:hover {
+                background: rgba($ios-danger, 0.1);
+                color: $ios-danger;
+              }
+            }
+          }
+        }
+
+        .tag-input-container {
+          display: flex;
+          gap: $ios-spacing-sm;
+
+          .tag-input {
+            flex: 1;
+            padding: 10px $ios-spacing-md;
+            border: 2px solid $ios-border;
+            border-radius: $ios-radius;
+            font-size: 14px;
+            background: $ios-background;
+            transition: $ios-transition;
+
+            &:focus {
+              outline: none;
+              border-color: $ios-primary;
+              box-shadow: 0 0 0 4px rgba(0, 122, 255, 0.1);
+            }
+
+            &::placeholder {
+              color: $ios-text-tertiary;
+            }
+          }
+
+          .add-tag-btn {
+            padding: 10px $ios-spacing-md;
+            background: $ios-secondary;
+            color: $ios-background;
+            border: none;
+            border-radius: $ios-radius;
+            cursor: pointer;
+            font-size: 14px;
+            font-weight: 500;
+            transition: $ios-transition;
+
+            &:hover {
+              background: darken($ios-secondary, 10%);
+              transform: translateY(-1px);
+            }
+
+            &:active {
+              transform: translateY(0);
+            }
+          }
+        }
+      }
+    }
+  }
+
+  // 底部提交区域
+  .panel-footer {
+    padding: $ios-spacing-md $ios-spacing-lg;
+    background: $ios-background-secondary;
+    border-top: 1px solid $ios-border;
+    flex-shrink: 0;
+
+    .submit-btn {
+      width: 100%;
+      padding: 12px 24px;
+      background: #007bff;
+      color: white;
+      border: none;
+      border-radius: 6px;
+      font-size: 16px;
+      font-weight: 500;
+      cursor: pointer;
+      transition: all 0.3s ease;
+
+      &:hover:not(:disabled) {
+        background: #0056b3;
+        transform: translateY(-1px);
+        box-shadow: 0 4px 12px rgba(0, 123, 255, 0.3);
+      }
+
+      &:active:not(:disabled) {
+        transform: translateY(0);
+      }
+
+      &:disabled {
+        background: #ccc;
+        cursor: not-allowed;
+        transform: none;
+        box-shadow: none;
+      }
+    }
+  }
+
+  // 响应式设计
+  @media (max-width: 768px) {
+    .panel-header {
+      padding: $ios-spacing-md;
+    }
+
+    .panel-content {
+      padding: $ios-spacing-md;
+    }
+
+    .panel-footer {
+      padding: $ios-spacing-md;
+    }
+  }
+}

+ 209 - 0
src/app/shared/components/consultation-order-panel/consultation-order-panel.component.ts

@@ -0,0 +1,209 @@
+import { Component, EventEmitter, Output, Input } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FormsModule, ReactiveFormsModule, FormBuilder, FormGroup, Validators } from '@angular/forms';
+import { MatChipsModule } from '@angular/material/chips';
+import { MatIconModule } from '@angular/material/icon';
+
+// 定义客户信息接口
+interface Customer {
+  id: string;
+  name: string;
+  phone: string;
+  wechat?: string;
+  avatar?: string;
+  customerType?: string;
+  source?: string;
+  remark?: string;
+  demandType?: string;
+  preferenceTags?: string[];
+  followUpStatus?: string;
+}
+
+@Component({
+  selector: 'app-consultation-order-panel',
+  standalone: true,
+  imports: [
+    CommonModule,
+    FormsModule,
+    ReactiveFormsModule,
+    MatChipsModule,
+    MatIconModule
+  ],
+  templateUrl: './consultation-order-panel.component.html',
+  styleUrls: ['./consultation-order-panel.component.scss']
+})
+export class ConsultationOrderPanelComponent {
+  @Input() roleContext: 'customer-service' | 'designer' | 'team-leader' = 'designer';
+  @Output() formSubmit = new EventEmitter<any>();
+
+  // 搜索客户关键词
+  searchKeyword = '';
+  // 搜索结果列表
+  searchResults: Customer[] = [];
+  // 选中的客户
+  selectedCustomer: Customer | null = null;
+  // 表单提交状态
+  isSubmitting = false;
+  // 项目需求卡片展开状态
+  isRequirementCardExpanded = false;
+
+  // 需求表单
+  requirementForm: FormGroup;
+  // 客户表单
+  customerForm: FormGroup;
+
+  // 样式选项
+  styleOptions = [
+    '现代简约', '北欧风', '工业风', '新中式', '法式轻奢', '日式', '美式', '混搭'
+  ];
+
+  // 项目小组选项
+  projectGroupOptions = [
+    '设计一组', '设计二组', '设计三组', '高端定制组', '软装设计组'
+  ];
+
+  // 偏好标签选项
+  preferenceTagOptions = [
+    '柔和色系', '明亮色系', '深色系', '中性色系',
+    '环保材料', '实木', '大理石', '瓷砖', '地板', '墙纸',
+    '现代简约', '北欧风格', '中式风格', '美式风格', '工业风',
+    '智能家电', '收纳空间', '开放式厨房', '大窗户'
+  ];
+
+  preferenceTags: string[] = [];
+  // 新标签输入
+  newTag = '';
+
+  constructor(private fb: FormBuilder) {
+    // 初始化需求表单
+    this.requirementForm = this.fb.group({
+      decorationType: ['', Validators.required],
+      downPayment: ['', [Validators.required, Validators.min(0)]],
+      firstDraftDate: ['', Validators.required],
+      style: [''],
+      projectGroup: [''],
+      budget: ['', Validators.required],
+      area: ['', [Validators.required, Validators.min(1)]],
+      houseType: [''],
+      floor: ['', Validators.min(1)],
+      preferredDesigner: [''],
+      specialRequirements: [''],
+      referenceCases: [[]],
+      priceDetails: [''],
+      // 新增字段
+      spaceRequirements: [''],
+      designAngles: [''],
+      specialAreaHandling: [''],
+      materialRequirements: [''],
+      lightingRequirements: ['']
+    });
+
+    // 初始化客户表单
+    this.customerForm = this.fb.group({
+      name: ['', Validators.required],
+      phone: ['', [Validators.required, Validators.pattern(/^1[3-9]\d{9}$/)]],
+      wechat: [''],
+      customerType: ['新客户'],
+      source: [''],
+      remark: [''],
+      demandType: [''],
+      followUpStatus: ['']
+    });
+  }
+
+  // 搜索客户
+  searchCustomer() {
+    if (this.searchKeyword.length >= 2) {
+      // 模拟搜索结果
+      this.searchResults = [
+        {
+          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"
+        }
+      ];
+    }
+  }
+
+  // 选择客户
+  selectCustomer(customer: Customer) {
+    this.selectedCustomer = customer;
+    // 填充客户表单
+    this.customerForm.patchValue({
+      name: customer.name,
+      phone: customer.phone,
+      wechat: customer.wechat || '',
+      customerType: customer.customerType || '新客户',
+      source: customer.source || '',
+      remark: customer.remark || ''
+    });
+    
+    // 清空搜索结果
+    this.searchResults = [];
+    this.searchKeyword = '';
+  }
+
+  // 清除选中的客户
+  clearSelectedCustomer() {
+    this.selectedCustomer = null;
+    this.customerForm.reset({
+      customerType: '新客户'
+    });
+  }
+
+  // 添加偏好标签
+  addPreferenceTag(tag: string): void {
+    if (tag && !this.preferenceTags.includes(tag)) {
+      this.preferenceTags.push(tag);
+      this.newTag = ''; // 清空输入框
+    }
+  }
+
+  // 删除偏好标签
+  removePreferenceTag(tag: string): void {
+    const index = this.preferenceTags.indexOf(tag);
+    if (index >= 0) {
+      this.preferenceTags.splice(index, 1);
+    }
+  }
+
+  // 提交表单
+  submitForm() {
+    if (this.customerForm.valid && this.requirementForm.valid) {
+      this.isSubmitting = true;
+      
+      const formData = {
+        customerInfo: this.customerForm.value,
+        requirementInfo: this.requirementForm.value,
+        preferenceTags: this.preferenceTags,
+        roleContext: this.roleContext, // 添加角色上下文信息
+        createdAt: new Date()
+      };
+
+      // 模拟提交请求
+      setTimeout(() => {
+        this.isSubmitting = false;
+        this.formSubmit.emit(formData);
+        
+        // 重置表单
+        this.customerForm.reset({
+          customerType: '新客户'
+        });
+        this.requirementForm.reset();
+        this.preferenceTags = [];
+        this.selectedCustomer = null;
+      }, 1000);
+    }
+  }
+}

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

@@ -0,0 +1,173 @@
+<div class="customer-review-card">
+  <!-- 统计数据概览 -->
+  <div class="stats-overview">
+    <h4>客户评价概览</h4>
+    <div class="stats-grid">
+      <div class="stat-item total">
+        <div class="stat-value">{{ stats().totalCount }}</div>
+        <div class="stat-label">总评价数</div>
+      </div>
+      <div class="stat-item score">
+        <div class="stat-value">{{ stats().averageScore }}<span class="score-suffix">/5</span></div>
+        <div class="stat-label">平均评分</div>
+        <div class="star-display">
+          @for (star of getStarRating(stats().averageScore); track $index) {
+            <span class="star">{{ star }}</span>
+          }
+        </div>
+      </div>
+      <div class="stat-item satisfied">
+        <div class="stat-value">{{ stats().satisfiedCount }}</div>
+        <div class="stat-label">满意评价</div>
+      </div>
+      <div class="stat-item pending">
+        <div class="stat-value">{{ stats().pendingCount }}</div>
+        <div class="stat-label">待处理</div>
+      </div>
+    </div>
+  </div>
+
+  <!-- 分类统计 -->
+  <div class="category-stats">
+    <h5>问题分类统计</h5>
+    <div class="category-grid">
+      @for (category of categories; track category.value) {
+        <div class="category-item">
+          <span class="category-label">{{ category.label }}</span>
+          <span class="category-count">{{ stats().categoryStats[category.value] || 0 }}</span>
+        </div>
+      }
+    </div>
+  </div>
+
+  <!-- 筛选区域 -->
+  <div class="filter-section">
+    <div class="filter-row">
+      <div class="filter-group">
+        <label>状态筛选:</label>
+        <div class="filter-buttons">
+          <button 
+            class="filter-btn"
+            [class.active]="statusFilter() === 'all'"
+            (click)="updateStatusFilter('all')">
+            全部
+          </button>
+          <button 
+            class="filter-btn pending"
+            [class.active]="statusFilter() === '待处理'"
+            (click)="updateStatusFilter('待处理')">
+            待处理
+          </button>
+          <button 
+            class="filter-btn satisfied"
+            [class.active]="statusFilter() === 'satisfied'"
+            (click)="updateStatusFilter('satisfied')">
+            满意
+          </button>
+          <button 
+            class="filter-btn unsatisfied"
+            [class.active]="statusFilter() === 'unsatisfied'"
+            (click)="updateStatusFilter('unsatisfied')">
+            不满意
+          </button>
+        </div>
+      </div>
+    </div>
+    
+    <div class="filter-row">
+      <div class="filter-group">
+        <label>分类筛选:</label>
+        <select 
+          class="filter-select"
+          [value]="categoryFilter()"
+          (change)="updateCategoryFilter($event)">
+          <option value="all">全部分类</option>
+          @for (category of categories; track category.value) {
+            <option [value]="category.value">{{ category.label }}</option>
+          }
+        </select>
+      </div>
+      
+      <div class="filter-group">
+        <label>评分筛选:</label>
+        <select 
+          class="filter-select"
+          [value]="scoreFilter()"
+          (change)="updateScoreFilter($event)">
+          <option value="all">全部评分</option>
+          <option value="high">高分 (4-5分)</option>
+          <option value="medium">中等 (2-4分)</option>
+          <option value="low">低分 (1-2分)</option>
+        </select>
+      </div>
+    </div>
+  </div>
+
+  <!-- 评价列表 -->
+  <div class="reviews-list">
+    @if (filteredFeedbacks() && filteredFeedbacks().length > 0) {
+      <div class="list-body">
+        @for (feedback of filteredFeedbacks(); track feedback.id) {
+          <div class="review-item" [class]="getStatusClass(feedback)">
+            <div class="review-header">
+              <div class="customer-info">
+                <span class="customer-name">{{ feedback.customerName || '客户' }}</span>
+                <span class="category-tag">{{ getCategoryLabel(getFeedbackCategory(feedback)) }}</span>
+              </div>
+              <div class="review-meta">
+                @if (feedback.rating !== undefined) {
+                  <div class="score-display" [class]="getScoreClass(feedback.rating)">
+                    <div class="score-stars">
+                      @for (star of getStarRating(feedback.rating); track $index) {
+                        <span class="star">{{ star }}</span>
+                      }
+                    </div>
+                    <span class="score-number">{{ feedback.rating }}/5</span>
+                  </div>
+                }
+                <span class="status-badge" [class]="getStatusClass(feedback)">
+                  {{ feedback.status }}
+                </span>
+              </div>
+            </div>
+            
+            <div class="review-content">
+              <p class="feedback-text">{{ feedback.content }}</p>
+              @if (feedback.problemLocation) {
+                <div class="problem-location">
+                  <strong>问题位置:</strong>{{ feedback.problemLocation }}
+                </div>
+              }
+              @if (feedback.referenceCase) {
+                <div class="reference-case">
+                  <strong>参考案例:</strong>{{ feedback.referenceCase }}
+                </div>
+              }
+            </div>
+            
+            <div class="review-footer">
+              <div class="time-info">
+                <span class="created-time">{{ feedback.createdAt | date:'yyyy-MM-dd HH:mm' }}</span>
+                @if (feedback.updatedAt && feedback.updatedAt !== feedback.createdAt) {
+                  <span class="updated-time">更新于 {{ feedback.updatedAt | date:'yyyy-MM-dd HH:mm' }}</span>
+                }
+              </div>
+              @if (feedback.response) {
+                <div class="response-section">
+                  <strong>处理回复:</strong>
+                  <p class="response-text">{{ feedback.response }}</p>
+                </div>
+              }
+            </div>
+          </div>
+        }
+      </div>
+    } @else {
+      <div class="empty-state">
+        <div class="empty-icon">💬</div>
+        <div class="empty-title">暂无客户评价</div>
+        <div class="empty-desc">当前筛选条件下没有找到相关的客户评价记录</div>
+      </div>
+    }
+  </div>
+</div>

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

@@ -0,0 +1,425 @@
+@use '../../styles/_ios-theme.scss' as *;
+
+:host { display: block; height: 100%; }
+
+@import '../../../../styles/variables';
+
+.customer-review-card {
+  padding: $ios-spacing-md;
+  background: $ios-background;
+  border-radius: 12px;
+  border: 1px solid $ios-border;
+
+  // 统计数据概览
+  .stats-overview {
+    margin-bottom: $ios-spacing-lg;
+    
+    h4 {
+      margin: 0 0 $ios-spacing-md 0;
+      font-size: $ios-font-size-lg;
+      font-weight: $ios-font-weight-semibold;
+      color: $ios-text-primary;
+    }
+
+    .stats-grid {
+      display: grid;
+      grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
+      gap: $ios-spacing-md;
+
+      .stat-item {
+        text-align: center;
+        padding: $ios-spacing-md;
+        background: $ios-background-secondary;
+        border-radius: $ios-border-radius-md;
+        border: 1px solid $ios-border;
+
+        .stat-value {
+          font-size: $ios-font-size-xl;
+          font-weight: $ios-font-weight-bold;
+          color: $ios-text-primary;
+          margin-bottom: $ios-spacing-xs;
+
+          .score-suffix {
+            font-size: $ios-font-size-sm;
+            color: $ios-text-secondary;
+          }
+        }
+
+        .stat-label {
+          font-size: $ios-font-size-xs;
+          color: $ios-text-secondary;
+          margin-bottom: $ios-spacing-xs;
+        }
+
+        .star-display {
+          .star {
+            color: #FFD700;
+            font-size: $ios-font-size-sm;
+          }
+        }
+
+        &.total .stat-value { color: $ios-primary; }
+        &.score .stat-value { color: #FF6B35; }
+        &.satisfied .stat-value { color: $ios-success; }
+        &.pending .stat-value { color: $ios-warning; }
+      }
+    }
+  }
+
+  // 分类统计
+  .category-stats {
+    margin-bottom: $ios-spacing-lg;
+    padding: $ios-spacing-md;
+    background: $ios-background-secondary;
+    border-radius: $ios-border-radius-md;
+    border: 1px solid $ios-border;
+
+    h5 {
+      margin: 0 0 $ios-spacing-md 0;
+      font-size: $ios-font-size-md;
+      font-weight: $ios-font-weight-semibold;
+      color: $ios-text-primary;
+    }
+
+    .category-grid {
+      display: grid;
+      grid-template-columns: repeat(auto-fit, minmax(100px, 1fr));
+      gap: $ios-spacing-sm;
+
+      .category-item {
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+        padding: $ios-spacing-sm;
+        background: $ios-background;
+        border-radius: $ios-border-radius-sm;
+        border: 1px solid $ios-border;
+
+        .category-label {
+          font-size: $ios-font-size-xs;
+          color: $ios-text-secondary;
+        }
+
+        .category-count {
+          font-size: $ios-font-size-sm;
+          font-weight: $ios-font-weight-semibold;
+          color: $ios-primary;
+        }
+      }
+    }
+  }
+
+  // 筛选区域
+  .filter-section {
+    margin-bottom: $ios-spacing-lg;
+    padding: $ios-spacing-md;
+    background: $ios-background-secondary;
+    border-radius: $ios-border-radius-md;
+    border: 1px solid $ios-border;
+
+    .filter-row {
+      display: flex;
+      flex-wrap: wrap;
+      gap: $ios-spacing-md;
+      margin-bottom: $ios-spacing-md;
+
+      &:last-child {
+        margin-bottom: 0;
+      }
+
+      .filter-group {
+        display: flex;
+        align-items: center;
+        gap: $ios-spacing-sm;
+
+        label {
+          font-size: $ios-font-size-sm;
+          color: $ios-text-secondary;
+          white-space: nowrap;
+        }
+
+        .filter-buttons {
+          display: flex;
+          gap: $ios-spacing-xs;
+
+          .filter-btn {
+            padding: $ios-spacing-xs $ios-spacing-sm;
+            border: 1px solid $ios-border;
+            background: $ios-background;
+            color: $ios-text-secondary;
+            border-radius: $ios-border-radius-sm;
+            font-size: $ios-font-size-xs;
+            cursor: pointer;
+            transition: all 0.2s ease;
+
+            &:hover {
+              background: $ios-background-secondary;
+            }
+
+            &.active {
+              background: $ios-primary;
+              color: white;
+              border-color: $ios-primary;
+            }
+
+            &.pending.active {
+              background: $ios-warning;
+              border-color: $ios-warning;
+            }
+
+            &.satisfied.active {
+              background: $ios-success;
+              border-color: $ios-success;
+            }
+
+            &.unsatisfied.active {
+              background: $ios-danger;
+              border-color: $ios-danger;
+            }
+          }
+        }
+
+        .filter-select {
+          padding: $ios-spacing-xs $ios-spacing-sm;
+          border: 1px solid $ios-border;
+          border-radius: $ios-border-radius-sm;
+          background: $ios-background;
+          color: $ios-text-primary;
+          font-size: $ios-font-size-xs;
+          min-width: 120px;
+
+          &:focus {
+            outline: none;
+            border-color: $ios-primary;
+          }
+        }
+      }
+    }
+  }
+
+  // 评价列表
+  .reviews-list {
+    .list-body {
+      .review-item {
+        padding: $ios-spacing-md;
+        margin-bottom: $ios-spacing-md;
+        background: $ios-background;
+        border: 1px solid $ios-border;
+        border-radius: $ios-border-radius-md;
+        transition: all 0.2s ease;
+
+        &:hover {
+          box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+        }
+
+        &:last-child {
+          margin-bottom: 0;
+        }
+
+        .review-header {
+          display: flex;
+          justify-content: space-between;
+          align-items: flex-start;
+          margin-bottom: $ios-spacing-md;
+
+          .customer-info {
+            display: flex;
+            align-items: center;
+            gap: $ios-spacing-sm;
+
+            .customer-name {
+              font-size: $ios-font-size-md;
+              font-weight: $ios-font-weight-semibold;
+              color: $ios-text-primary;
+            }
+
+            .category-tag {
+              padding: $ios-spacing-xs $ios-spacing-sm;
+              background: $ios-primary;
+              color: white;
+              border-radius: $ios-border-radius-sm;
+              font-size: $ios-font-size-xs;
+            }
+          }
+
+          .review-meta {
+            display: flex;
+            align-items: center;
+            gap: $ios-spacing-sm;
+
+            .score-display {
+              display: flex;
+              align-items: center;
+              gap: $ios-spacing-xs;
+
+              .score-stars {
+                .star {
+                  color: #FFD700;
+                  font-size: $ios-font-size-sm;
+                }
+              }
+
+              .score-number {
+                font-size: $ios-font-size-xs;
+                color: $ios-text-secondary;
+              }
+
+              &.high .score-number { color: $ios-success; }
+              &.medium .score-number { color: $ios-warning; }
+              &.low .score-number { color: $ios-danger; }
+            }
+
+            .status-badge {
+              padding: $ios-spacing-xs $ios-spacing-sm;
+              border-radius: $ios-border-radius-sm;
+              font-size: $ios-font-size-xs;
+              font-weight: $ios-font-weight-semibold;
+
+              &.satisfied {
+                background: rgba($ios-success, 0.1);
+                color: $ios-success;
+              }
+
+              &.unsatisfied {
+                background: rgba($ios-danger, 0.1);
+                color: $ios-danger;
+              }
+
+              &.pending {
+                background: rgba($ios-warning, 0.1);
+                color: $ios-warning;
+              }
+            }
+          }
+        }
+
+        .review-content {
+          margin-bottom: $ios-spacing-md;
+
+          .feedback-text {
+            margin: 0 0 $ios-spacing-sm 0;
+            color: $ios-text-primary;
+            line-height: 1.5;
+          }
+
+          .problem-location {
+            margin-bottom: $ios-spacing-sm;
+            padding: $ios-spacing-sm;
+            background: rgba($ios-warning, 0.1);
+            border-radius: $ios-border-radius-sm;
+            font-size: $ios-font-size-sm;
+            color: $ios-text-secondary;
+
+            strong {
+              color: $ios-warning;
+            }
+          }
+
+          .feedback-images {
+            display: flex;
+            gap: $ios-spacing-sm;
+            flex-wrap: wrap;
+
+            .feedback-image {
+              width: 80px;
+              height: 80px;
+              object-fit: cover;
+              border-radius: $ios-border-radius-sm;
+              border: 1px solid $ios-border;
+            }
+          }
+        }
+
+        .review-footer {
+          .time-info {
+            display: flex;
+            gap: $ios-spacing-md;
+            margin-bottom: $ios-spacing-sm;
+
+            .created-time,
+            .updated-time {
+              font-size: $ios-font-size-xs;
+              color: $ios-text-secondary;
+            }
+
+            .updated-time {
+              color: $ios-primary;
+            }
+          }
+
+          .response-section {
+            padding: $ios-spacing-sm;
+            background: rgba($ios-primary, 0.05);
+            border-radius: $ios-border-radius-sm;
+            border-left: 3px solid $ios-primary;
+
+            strong {
+              color: $ios-primary;
+              font-size: $ios-font-size-sm;
+            }
+
+            .response-text {
+              margin: $ios-spacing-xs 0 0 0;
+              color: $ios-text-secondary;
+              font-size: $ios-font-size-sm;
+              line-height: 1.4;
+            }
+          }
+        }
+      }
+    }
+
+    .empty-state {
+      text-align: center;
+      padding: $ios-spacing-xl;
+      color: $ios-text-secondary;
+
+      .empty-icon {
+        font-size: 48px;
+        margin-bottom: $ios-spacing-md;
+      }
+
+      .empty-title {
+        font-size: $ios-font-size-lg;
+        font-weight: $ios-font-weight-semibold;
+        color: $ios-text-primary;
+        margin-bottom: $ios-spacing-sm;
+      }
+
+      .empty-desc {
+        font-size: $ios-font-size-sm;
+        color: $ios-text-secondary;
+      }
+    }
+  }
+
+  // 响应式设计
+  @media (max-width: 768px) {
+    .stats-overview .stats-grid {
+      grid-template-columns: repeat(2, 1fr);
+    }
+
+    .category-stats .category-grid {
+      grid-template-columns: 1fr;
+    }
+
+    .filter-section .filter-row {
+      flex-direction: column;
+      align-items: stretch;
+
+      .filter-group {
+        flex-direction: column;
+        align-items: stretch;
+
+        .filter-buttons {
+          justify-content: center;
+        }
+      }
+    }
+
+    .reviews-list .review-item .review-header {
+      flex-direction: column;
+      gap: $ios-spacing-sm;
+    }
+  }
+}

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

@@ -0,0 +1,175 @@
+import { Component, Input, computed, signal } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FormsModule } from '@angular/forms';
+import { CustomerFeedback } from '../../../models/project.model';
+
+export interface ReviewStats {
+  totalCount: number;
+  averageScore: number;
+  satisfiedCount: number;
+  unsatisfiedCount: number;
+  pendingCount: number;
+  processedCount: number;
+  categoryStats: { [key: string]: number };
+}
+
+@Component({
+  selector: 'app-customer-review-card',
+  standalone: true,
+  imports: [CommonModule, FormsModule],
+  templateUrl: './customer-review-card.html',
+  styleUrls: ['./customer-review-card.scss']
+})
+export class CustomerReviewCardComponent {
+  @Input() feedbacks: CustomerFeedback[] = [];
+  
+  // 筛选条件
+  statusFilter = signal<string>('all');
+  categoryFilter = signal<string>('all');
+  scoreFilter = signal<string>('all');
+  
+  // 评价分类
+  categories = [
+    { value: 'color', label: '色彩问题' },
+    { value: 'furniture', label: '家具款式问题' },
+    { value: 'lighting', label: '光线问题' },
+    { value: 'layout', label: '布局问题' },
+    { value: 'material', label: '材质问题' },
+    { value: 'other', label: '其他问题' }
+  ];
+  
+  // 计算统计数据
+  stats = computed<ReviewStats>(() => {
+    const feedbacks = this.feedbacks || [];
+    
+    const categoryStats: { [key: string]: number } = {};
+    this.categories.forEach(cat => {
+      categoryStats[cat.value] = feedbacks.filter(f => this.getFeedbackCategory(f) === cat.value).length;
+    });
+    
+    const scores = feedbacks.filter(f => f.rating !== undefined).map(f => f.rating || 0);
+    const averageScore = scores.length > 0 ? scores.reduce((sum, score) => sum + score, 0) / scores.length : 0;
+    
+    return {
+      totalCount: feedbacks.length,
+      averageScore: Math.round(averageScore * 10) / 10,
+      satisfiedCount: feedbacks.filter(f => f.isSatisfied).length,
+      unsatisfiedCount: feedbacks.filter(f => !f.isSatisfied).length,
+      pendingCount: feedbacks.filter(f => f.status === '待处理').length,
+      processingCount: feedbacks.filter(f => f.status === '处理中').length,
+      processedCount: feedbacks.filter(f => f.status === '已解决').length,
+      categoryStats
+    };
+  });
+  
+  // 筛选后的评价列表
+  filteredFeedbacks = computed(() => {
+    let filtered = this.feedbacks || [];
+    
+    // 状态筛选
+    const status = this.statusFilter();
+    if (status !== 'all') {
+      if (status === 'satisfied') {
+        filtered = filtered.filter(f => f.isSatisfied);
+      } else if (status === 'unsatisfied') {
+        filtered = filtered.filter(f => !f.isSatisfied);
+      } else {
+        filtered = filtered.filter(f => f.status === status);
+      }
+    }
+    
+    // 分类筛选
+    const category = this.categoryFilter();
+    if (category !== 'all') {
+      filtered = filtered.filter(f => this.getFeedbackCategory(f) === category);
+    }
+    
+    // 评分筛选
+    const score = this.scoreFilter();
+    if (score !== 'all') {
+      if (score === 'high') {
+        filtered = filtered.filter(f => (f.rating || 0) >= 4);
+      } else if (score === 'medium') {
+        filtered = filtered.filter(f => (f.rating || 0) >= 2 && (f.rating || 0) < 4);
+      } else if (score === 'low') {
+        filtered = filtered.filter(f => (f.rating || 0) < 2);
+      }
+    }
+    
+    return filtered.sort((a, b) => {
+      // 按状态排序:待处理 > 已处理,按时间倒序
+      if (a.status !== b.status) {
+        if (a.status === '待处理') return -1;
+        if (b.status === '待处理') return 1;
+      }
+      return new Date(b.updatedAt || b.createdAt).getTime() - new Date(a.updatedAt || a.createdAt).getTime();
+    });
+  });
+  
+  // 获取反馈分类
+  getFeedbackCategory(feedback: CustomerFeedback): string {
+    const content = feedback.content.toLowerCase();
+    const location = (feedback.problemLocation || '').toLowerCase();
+    
+    if (content.includes('色彩') || content.includes('颜色') || location.includes('色彩')) {
+      return 'color';
+    } else if (content.includes('家具') || content.includes('沙发') || content.includes('桌子') || location.includes('家具')) {
+      return 'furniture';
+    } else if (content.includes('光线') || content.includes('照明') || content.includes('灯光')) {
+      return 'lighting';
+    } else if (content.includes('布局') || content.includes('空间') || content.includes('摆放')) {
+      return 'layout';
+    } else if (content.includes('材质') || content.includes('质感') || content.includes('纹理')) {
+      return 'material';
+    }
+    return 'other';
+  }
+  
+  // 获取分类标签
+  getCategoryLabel(category: string): string {
+    const cat = this.categories.find(c => c.value === category);
+    return cat ? cat.label : '其他问题';
+  }
+  
+  // 获取评分星级
+  getStarRating(score: number): string[] {
+    const stars = [];
+    for (let i = 1; i <= 5; i++) {
+      if (i <= score) {
+        stars.push('★');
+      } else if (i - 0.5 <= score) {
+        stars.push('☆');
+      } else {
+        stars.push('☆');
+      }
+    }
+    return stars;
+  }
+  
+  // 获取状态样式类
+  getStatusClass(feedback: CustomerFeedback): string {
+    if (feedback.status === '已解决') return 'processed';
+    if (feedback.status === '处理中') return 'processing';
+    return 'pending';
+  }
+  
+  // 获取评分样式类
+  getScoreClass(score: number): string {
+    if (score >= 4) return 'high';
+    if (score >= 2) return 'medium';
+    return 'low';
+  }
+  
+  // 更新筛选条件
+  updateStatusFilter(status: string): void {
+    this.statusFilter.set(status);
+  }
+
+  updateCategoryFilter(event: any): void {
+    this.categoryFilter.set(event.target.value);
+  }
+
+  updateScoreFilter(event: any): void {
+    this.scoreFilter.set(event.target.value);
+  }
+}

+ 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();
+  }
+}

+ 420 - 0
src/app/shared/components/process-progress-bar/process-progress-bar.component.scss

@@ -0,0 +1,420 @@
+// 流程进度条组件样式
+.process-progress-bar {
+  width: 100%;
+  padding: 24px 0;
+  
+  // 进度概览
+  .progress-overview {
+    margin-bottom: 32px;
+    padding: 20px;
+    background: linear-gradient(135deg, #f8f9fa 0%, #ffffff 100%);
+    border-radius: 12px;
+    border: 1px solid #e9ecef;
+    
+    .progress-info {
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      margin-bottom: 12px;
+      
+      .progress-label {
+        font-size: 16px;
+        font-weight: 600;
+        color: #495057;
+      }
+      
+      .progress-percentage {
+        font-size: 24px;
+        font-weight: 700;
+        color: #1976d2;
+      }
+    }
+    
+    .progress-bar-overview {
+      height: 8px;
+      background: #e9ecef;
+      border-radius: 4px;
+      overflow: hidden;
+      
+      .progress-fill {
+        height: 100%;
+        background: linear-gradient(90deg, #1976d2, #42a5f5);
+        border-radius: 4px;
+        transition: width 0.8s cubic-bezier(0.4, 0, 0.2, 1);
+        box-shadow: 0 2px 4px rgba(25, 118, 210, 0.3);
+      }
+    }
+  }
+  
+  .progress-stages-container {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    position: relative;
+    width: 100%;
+    
+    .stage-wrapper {
+      display: flex;
+      align-items: center;
+      flex: 1;
+      
+      &:last-child {
+        flex: none;
+      }
+      
+      .stage-node {
+          display: flex;
+          flex-direction: column;
+          align-items: center;
+          min-width: 140px;
+          padding: 20px 16px;
+          border-radius: 16px;
+          transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
+          position: relative;
+          background: #ffffff;
+          border: 2px solid transparent;
+          box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
+          
+          // 确保点击区域不小于44x44像素
+          min-height: 44px;
+          
+          &.clickable {
+            cursor: pointer;
+            
+            &:hover {
+              transform: translateY(-3px);
+              box-shadow: 0 12px 28px rgba(0, 0, 0, 0.15);
+              border-color: rgba(25, 118, 210, 0.3);
+            }
+            
+            &:active {
+              transform: translateY(-1px);
+              transition-duration: 0.1s;
+            }
+            
+            &:focus {
+              outline: none;
+              box-shadow: 0 0 0 3px rgba(25, 118, 210, 0.2);
+            }
+          }
+        
+        // 待开始状态
+        &.pending {
+          background: #f8f9fa;
+          border-color: #e9ecef;
+          
+          .stage-icon {
+            background: #e9ecef;
+            color: #6c757d;
+          }
+          
+          .stage-content {
+            .stage-title {
+              color: #6c757d;
+            }
+            
+            .stage-description {
+              color: #adb5bd;
+            }
+            
+            .stage-status-indicator.status-pending {
+              background: #f8f9fa;
+              color: #6c757d;
+              border-color: #e9ecef;
+            }
+          }
+        }
+        
+        // 进行中状态
+        &.in-progress {
+          background: linear-gradient(135deg, #fff3e0 0%, #ffffff 100%);
+          border-color: #ff9800;
+          box-shadow: 0 4px 12px rgba(255, 152, 0, 0.15);
+          
+          .stage-icon {
+            background: linear-gradient(135deg, #ff9800, #f57c00);
+            color: #ffffff;
+            
+            .progress-dot {
+              animation: pulse 2s infinite;
+            }
+          }
+          
+          .stage-content {
+            .stage-title {
+              color: #e65100;
+              font-weight: 600;
+            }
+            
+            .stage-description {
+              color: #f57c00;
+            }
+            
+            .stage-status-indicator.status-progress {
+              background: linear-gradient(135deg, #ff9800, #f57c00);
+              color: #ffffff;
+              border-color: #ff9800;
+            }
+          }
+        }
+        
+        // 已完成状态
+        &.completed {
+          background: linear-gradient(135deg, #e8f5e9 0%, #ffffff 100%);
+          border-color: #4caf50;
+          box-shadow: 0 4px 12px rgba(76, 175, 80, 0.15);
+          
+          .stage-icon {
+            background: linear-gradient(135deg, #4caf50, #388e3c);
+            color: #ffffff;
+          }
+          
+          .stage-content {
+            .stage-title {
+              color: #2e7d32;
+              font-weight: 600;
+            }
+            
+            .stage-description {
+              color: #388e3c;
+            }
+            
+            .stage-status-indicator.status-completed {
+              background: linear-gradient(135deg, #4caf50, #388e3c);
+              color: #ffffff;
+              border-color: #4caf50;
+            }
+          }
+        }
+        
+        .stage-icon {
+          width: 48px;
+          height: 48px;
+          border-radius: 50%;
+          display: flex;
+          align-items: center;
+          justify-content: center;
+          margin-bottom: 12px;
+          transition: all 0.3s ease;
+          
+          svg {
+            width: 20px;
+            height: 20px;
+          }
+          
+          .progress-dot {
+            width: 10px;
+            height: 10px;
+            border-radius: 50%;
+            background: #ffffff;
+          }
+          
+          .pending-dot {
+            width: 8px;
+            height: 8px;
+            border-radius: 50%;
+            background: #6c757d;
+          }
+        }
+        
+        .stage-content {
+          text-align: center;
+          width: 100%;
+          
+          .stage-title {
+            font-size: 15px;
+            font-weight: 500;
+            margin-bottom: 6px;
+            line-height: 1.3;
+            transition: all 0.3s ease;
+          }
+          
+          .stage-description {
+            font-size: 12px;
+            margin-bottom: 8px;
+            line-height: 1.4;
+            opacity: 0.8;
+            transition: all 0.3s ease;
+          }
+          
+          .stage-status-indicator {
+            font-size: 11px;
+            padding: 6px 12px;
+            border-radius: 16px;
+            border: 1px solid;
+            font-weight: 500;
+            transition: all 0.3s ease;
+            display: inline-block;
+          }
+        }
+      }
+      
+      .stage-connector {
+        flex: 1;
+        height: 3px;
+        margin: 0 12px;
+        position: relative;
+        display: flex;
+        align-items: center;
+        
+        .connector-line {
+          flex: 1;
+          height: 3px;
+          background: #e9ecef;
+          border-radius: 2px;
+          transition: all 0.6s ease;
+        }
+        
+        .connector-arrow {
+          margin-left: 8px;
+          color: #e9ecef;
+          transition: all 0.6s ease;
+          
+          svg {
+            width: 12px;
+            height: 12px;
+          }
+        }
+        
+        &.connector-completed {
+          .connector-line {
+            background: linear-gradient(90deg, #4caf50, #66bb6a);
+            box-shadow: 0 1px 3px rgba(76, 175, 80, 0.3);
+          }
+          
+          .connector-arrow {
+            color: #4caf50;
+          }
+        }
+      }
+    }
+  }
+}
+
+// 脉冲动画
+@keyframes pulse {
+  0% {
+    transform: scale(1);
+    opacity: 1;
+  }
+  50% {
+    transform: scale(1.2);
+    opacity: 0.7;
+  }
+  100% {
+    transform: scale(1);
+    opacity: 1;
+  }
+}
+
+// 响应式设计
+@media (max-width: 1200px) {
+  .process-progress-bar {
+    .progress-stages-container {
+      .stage-wrapper {
+        .stage-node {
+          min-width: 100px;
+          padding: 12px 8px;
+          
+          .stage-title {
+            font-size: 13px;
+          }
+          
+          .stage-icon {
+            width: 36px;
+            height: 36px;
+          }
+        }
+      }
+    }
+  }
+}
+
+@media (max-width: 992px) {
+  .process-progress-bar {
+    .progress-stages-container {
+      .stage-wrapper {
+        .stage-node {
+          min-width: 80px;
+          padding: 10px 6px;
+          
+          .stage-title {
+            font-size: 12px;
+          }
+          
+          .stage-icon {
+            width: 32px;
+            height: 32px;
+          }
+          
+          .stage-status-indicator {
+            font-size: 10px;
+            padding: 3px 6px;
+          }
+        }
+        
+        .stage-connector {
+          margin: 0 4px;
+        }
+      }
+    }
+  }
+}
+
+@media (max-width: 768px) {
+  .process-progress-bar {
+    padding: 16px 0;
+    
+    .progress-stages-container {
+      flex-direction: column;
+      align-items: stretch;
+      
+      .stage-wrapper {
+        flex-direction: column;
+        margin-bottom: 16px;
+        
+        &:last-child {
+          margin-bottom: 0;
+        }
+        
+        .stage-node {
+          width: 100%;
+          min-width: auto;
+          flex-direction: row;
+          justify-content: flex-start;
+          padding: 16px;
+          text-align: left;
+          
+          .stage-icon {
+            margin-right: 12px;
+            margin-bottom: 0;
+          }
+          
+          .stage-content {
+            flex: 1;
+            
+            .stage-title {
+              text-align: left;
+              margin-bottom: 4px;
+            }
+          }
+          
+          .stage-status-indicator {
+            margin-left: auto;
+          }
+        }
+        
+        .stage-connector {
+          width: 2px;
+          height: 20px;
+          margin: 0 auto;
+          
+          .connector-line {
+            width: 2px;
+            height: 100%;
+          }
+        }
+      }
+    }
+  }
+}

+ 189 - 0
src/app/shared/components/process-progress-bar/process-progress-bar.ts

@@ -0,0 +1,189 @@
+import { Component, Input, Output, EventEmitter, signal, computed, OnInit, OnChanges, SimpleChanges } from '@angular/core';
+import { CommonModule } from '@angular/common';
+
+// 定义流程阶段接口
+export interface ProcessStage {
+  id: string;
+  title: string;
+  status: 'pending' | 'in-progress' | 'completed';
+  icon?: string;
+  description?: string;
+  clickable?: boolean;
+}
+
+@Component({
+  selector: 'app-process-progress-bar',
+  standalone: true,
+  imports: [CommonModule],
+  template: `
+    <div class="process-progress-bar">
+      <!-- 进度概览 -->
+      <div class="progress-overview">
+        <div class="progress-info">
+          <span class="progress-label">整体进度</span>
+          <span class="progress-percentage">{{ completionPercentage() }}%</span>
+        </div>
+        <div class="progress-bar-overview">
+          <div class="progress-fill" [style.width.%]="completionPercentage()"></div>
+        </div>
+      </div>
+      
+      <!-- 横向串联式流程状态显示条 -->
+      <div class="progress-stages-container">
+        @for (stage of stages; track stage.id; let i = $index) {
+          <div class="stage-wrapper">
+            <!-- 阶段节点 -->
+            <div class="stage-node" 
+                 [class.pending]="stage.status === 'pending'"
+                 [class.in-progress]="stage.status === 'in-progress'"
+                 [class.completed]="stage.status === 'completed'"
+                 [class.clickable]="clickable()"
+                 [attr.aria-label]="getAriaLabel(stage)"
+                 [attr.tabindex]="clickable() ? 0 : -1"
+                 (click)="onStageClick(stage)"
+                 (keydown)="onStageKeydown($event, stage)"
+                 (mouseenter)="onStageHover(stage, true)"
+                 (mouseleave)="onStageHover(stage, false)">
+              
+              <!-- 状态图标 -->
+              <div class="stage-icon">
+                @if (stage.status === 'completed') {
+                  <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
+                    <path d="m9 12 2 2 4-4" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+                  </svg>
+                } @else if (stage.status === 'in-progress') {
+                  <div class="progress-dot"></div>
+                } @else {
+                  <div class="pending-dot"></div>
+                }
+              </div>
+              
+              <!-- 阶段内容 -->
+              <div class="stage-content">
+                <!-- 阶段标题 -->
+                <div class="stage-title">{{ stage.title }}</div>
+                
+                <!-- 阶段描述 -->
+                @if (stage.description) {
+                  <div class="stage-description">{{ stage.description }}</div>
+                }
+                
+                <!-- 状态指示器 -->
+                <div class="stage-status-indicator" 
+                     [class.status-pending]="stage.status === 'pending'"
+                     [class.status-progress]="stage.status === 'in-progress'"
+                     [class.status-completed]="stage.status === 'completed'">
+                  {{ getStatusText(stage.status) }}
+                </div>
+              </div>
+            </div>
+            
+            <!-- 连接线 -->
+            @if (i < stages.length - 1) {
+              <div class="stage-connector" 
+                   [class.connector-completed]="isConnectorCompleted(i)">
+                <div class="connector-line"></div>
+                <div class="connector-arrow">
+                  <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor">
+                    <polyline points="9 18 15 12 9 6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+                  </svg>
+                </div>
+              </div>
+            }
+          </div>
+        }
+      </div>
+    </div>
+  `,
+  styleUrls: ['./process-progress-bar.component.scss']
+})
+export class ProcessProgressBarComponent implements OnInit, OnChanges {
+  @Input() stages: ProcessStage[] = [];
+  @Input() clickable = signal<boolean>(true);
+  
+  @Output() stageClick = new EventEmitter<ProcessStage>();
+  @Output() stageHover = new EventEmitter<{stage: ProcessStage, isHover: boolean}>();
+  
+  ngOnInit(): void {
+    console.log('ProcessProgressBarComponent initialized');
+    console.log('Initial stages:', this.stages);
+    console.log('Initial stages length:', this.stages?.length || 0);
+    
+    if (!this.stages) {
+      console.warn('Stages input is null or undefined');
+      this.stages = [];
+    }
+  }
+  
+  ngOnChanges(changes: SimpleChanges): void {
+    if (changes['stages']) {
+      console.log('ProcessProgressBarComponent stages changed');
+      console.log('New stages:', changes['stages'].currentValue);
+      console.log('New stages length:', changes['stages'].currentValue?.length || 0);
+      
+      if (!changes['stages'].currentValue) {
+        console.warn('New stages value is null or undefined');
+        this.stages = [];
+      }
+    }
+  }
+  
+  completionPercentage(): number {
+    if (!this.stages || this.stages.length === 0) {
+      console.log('No stages available for completion calculation');
+      return 0;
+    }
+    
+    const completedCount = this.stages.filter(stage => {
+      if (!stage) {
+        console.warn('Found null stage in completion calculation');
+        return false;
+      }
+      return stage.status === 'completed';
+    }).length;
+    
+    const percentage = Math.round((completedCount / this.stages.length) * 100);
+    console.log(`Completion percentage: ${percentage}% (${completedCount}/${this.stages.length})`);
+    return percentage;
+  }
+  
+  onStageClick(stage: ProcessStage): void {
+    if (this.clickable()) {
+      this.stageClick.emit(stage);
+    }
+  }
+  
+  onStageKeydown(event: KeyboardEvent, stage: ProcessStage): void {
+    if (event.key === 'Enter' || event.key === ' ') {
+      event.preventDefault();
+      this.onStageClick(stage);
+    }
+  }
+  
+  onStageHover(stage: ProcessStage, isHover: boolean): void {
+    this.stageHover.emit({ stage, isHover });
+  }
+  
+  isConnectorCompleted(index: number): boolean {
+    const currentStage = this.stages[index];
+    const nextStage = this.stages[index + 1];
+    return currentStage.status === 'completed' && nextStage && nextStage.status !== 'pending';
+  }
+  
+  getAriaLabel(stage: ProcessStage): string {
+    return `${stage.title} - ${this.getStatusText(stage.status)}${stage.description ? ': ' + stage.description : ''}`;
+  }
+  
+  getStatusText(status: string): string {
+    switch (status) {
+      case 'completed':
+        return '已完成';
+      case 'in-progress':
+        return '进行中';
+      case 'pending':
+        return '未开始';
+      default:
+        return '未知';
+    }
+  }
+}

+ 84 - 0
src/app/shared/components/process-status-bar/process-status-bar.component.scss

@@ -0,0 +1,84 @@
+.process-status-bar {
+  display: flex;
+  align-items: center;
+  gap: 8px;
+  flex-wrap: nowrap;
+  
+  .status-item {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    padding: 6px 12px;
+    border-radius: 16px;
+    font-size: 12px;
+    font-weight: 500;
+    white-space: nowrap;
+    transition: all 0.3s ease;
+    min-width: 60px;
+    
+    .status-text {
+      color: white;
+      text-align: center;
+    }
+    
+    // 已完成状态 - 绿色背景
+    &.status-completed {
+      background-color: #10b981;
+      box-shadow: 0 2px 4px rgba(16, 185, 129, 0.2);
+      
+      &:hover {
+        background-color: #059669;
+        transform: translateY(-1px);
+        box-shadow: 0 4px 8px rgba(16, 185, 129, 0.3);
+      }
+    }
+    
+    // 进行中状态 - 红色背景
+    &.status-in-progress {
+      background-color: #ef4444;
+      box-shadow: 0 2px 4px rgba(239, 68, 68, 0.2);
+      
+      &:hover {
+        background-color: #dc2626;
+        transform: translateY(-1px);
+        box-shadow: 0 4px 8px rgba(239, 68, 68, 0.3);
+      }
+    }
+    
+    // 待完成状态 - 黄色背景
+    &.status-pending {
+      background-color: #f59e0b;
+      box-shadow: 0 2px 4px rgba(245, 158, 11, 0.2);
+      
+      &:hover {
+        background-color: #d97706;
+        transform: translateY(-1px);
+        box-shadow: 0 4px 8px rgba(245, 158, 11, 0.3);
+      }
+    }
+  }
+  
+  .status-separator {
+    width: 8px;
+    height: 1px;
+    background-color: #e5e7eb;
+    flex-shrink: 0;
+  }
+}
+
+// 响应式设计
+@media (max-width: 768px) {
+  .process-status-bar {
+    gap: 4px;
+    
+    .status-item {
+      padding: 4px 8px;
+      font-size: 11px;
+      min-width: 50px;
+    }
+    
+    .status-separator {
+      width: 4px;
+    }
+  }
+}

+ 44 - 0
src/app/shared/components/process-status-bar/process-status-bar.ts

@@ -0,0 +1,44 @@
+import { Component, Input, signal, computed } from '@angular/core';
+import { CommonModule } from '@angular/common';
+
+// 定义流程阶段状态接口
+export interface ProcessStatusStage {
+  id: string;
+  name: string;
+  status: 'pending' | 'in-progress' | 'completed';
+}
+
+@Component({
+  selector: 'app-process-status-bar',
+  standalone: true,
+  imports: [CommonModule],
+  template: `
+    <div class="process-status-bar">
+      @for (stage of stages(); track stage.id; let i = $index) {
+        <div class="status-item" 
+             [class.status-completed]="stage.status === 'completed'"
+             [class.status-in-progress]="stage.status === 'in-progress'"
+             [class.status-pending]="stage.status === 'pending'">
+          <span class="status-text">{{ stage.name }}</span>
+        </div>
+        @if (i < stages().length - 1) {
+          <div class="status-separator"></div>
+        }
+      }
+    </div>
+  `,
+  styleUrls: ['./process-status-bar.component.scss']
+})
+export class ProcessStatusBarComponent {
+  @Input() set stageData(value: ProcessStatusStage[]) {
+    console.log('ProcessStatusBarComponent 接收到数据:', value);
+    this.stages.set(value || []);
+  }
+
+  // 内部状态管理
+  stages = signal<ProcessStatusStage[]>([]);
+
+  constructor() {
+    console.log('ProcessStatusBarComponent 构造函数被调用');
+  }
+}

+ 12 - 0
src/app/shared/components/progress-bar/progress-bar.component.html

@@ -0,0 +1,12 @@
+<div class="progress-bar">
+  <div class="progress-sections">
+    @for (section of sections; track section.key; let i = $index) {
+      <div class="section" [class]="getSectionStatus(section.key)">
+        <div class="label">{{section.label}}</div>
+        @if (i < sections.length - 1) {
+          <div class="connector"></div>
+        }
+      </div>
+    }
+  </div>
+</div>

+ 23 - 0
src/app/shared/components/progress-bar/progress-bar.component.ts

@@ -0,0 +1,23 @@
+import { Component, Input } from '@angular/core';
+import { CommonModule } from '@angular/common';
+
+export interface ProgressSection {
+  key: string;
+  label: string;
+}
+
+@Component({
+  selector: 'app-progress-bar',
+  standalone: true,
+  imports: [CommonModule],
+  templateUrl: './progress-bar.component.html',
+  styles: [`
+    :host {
+      display: block;
+    }
+  `]
+})
+export class ProgressBarComponent {
+  @Input() sections: ProgressSection[] = [];
+  @Input() getSectionStatus!: (key: string) => 'completed' | 'active' | 'pending';
+}

+ 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;
+}

+ 49 - 0
src/app/shared/components/requirements-confirm-card/README.md

@@ -0,0 +1,49 @@
+# Requirements Confirm Card 布局方案
+
+## 概述
+
+本组件提供了两种布局方案用于"制作流程进度>需求沟通"部分的参考图片和CAD图纸上传功能。
+
+## 方案一:简洁并排布局(当前使用)
+
+- **文件**: `requirements-confirm-card.html`
+- **样式**: `requirements-confirm-card.scss`
+- **特点**:
+  - 文本描述区域独立一行
+  - 参考图片和CAD图纸并排显示
+  - 简洁实用的设计风格
+  - 响应式布局,移动端自动变为单列
+
+## 方案二:美观卡片式布局(备选方案)
+
+- **文件**: `requirements-confirm-card-alternative.html`
+- **样式**: `requirements-confirm-card-alternative.scss`
+- **特点**:
+  - 现代化卡片式设计
+  - 渐变色彩和图标装饰
+  - 悬停动画效果
+  - 深色模式支持
+  - 更丰富的视觉层次
+
+## 使用方法
+
+### 切换到备选方案
+
+如需使用美观的卡片式布局,请按以下步骤操作:
+
+1. 将 `requirements-confirm-card-alternative.html` 的内容复制到 `requirements-confirm-card.html`
+2. 在组件的 HTML 模板中,将 `materials-section` 的 class 改为 `materials-section alternative-layout`
+
+### 响应式特性
+
+两种方案都支持响应式设计:
+
+- **桌面端**: 参考图片和CAD图纸并排显示
+- **移动端**: 自动切换为单列布局
+
+## 技术实现
+
+- 使用 CSS Grid 布局实现响应式设计
+- 采用 Angular 控制流指令 `@if` 和 `@for`
+- 支持文件拖拽上传和点击上传
+- 包含文件类型验证和预览功能

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

@@ -0,0 +1,83 @@
+<!-- 备选方案:美观的卡片式布局 -->
+<div class="materials-section alternative-layout">
+  <!-- 文本输入区域 - 独立一行 -->
+  <div class="text-upload-section">
+    <div class="upload-item text-item">
+      <h5>文本描述</h5>
+      <form [formGroup]="materialUploadForm" (ngSubmit)="onTextSubmit()">
+        <textarea 
+          formControlName="textContent" 
+          placeholder="请描述您的装修需求,如:希望温馨的暖木色调,适合亲子家庭,需要瑜伽区收纳..."
+          rows="4">
+        </textarea>
+        <button type="submit" class="btn-primary btn-sm" [disabled]="!materialUploadForm.get('textContent')?.value">
+          解析文本
+        </button>
+      </form>
+    </div>
+  </div>
+
+  <!-- 参考图片和CAD图纸 - 美观卡片式并排布局 -->
+  <div class="file-upload-cards">
+    <!-- 参考图片上传卡片 -->
+    <div class="upload-card image-card">
+      <div class="card-header">
+        <div class="card-icon image-icon">
+          <svg width="20" height="20" 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>
+        </div>
+        <h5>参考图片</h5>
+      </div>
+      <div class="card-body">
+        <div class="file-upload-zone" (click)="imageInput.click()">
+          <div class="upload-content">
+            <div class="upload-icon">
+              <svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
+                <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
+                <polyline points="7,10 12,15 17,10"></polyline>
+                <line x1="12" y1="15" x2="12" y2="3"></line>
+              </svg>
+            </div>
+            <p>点击上传参考图片</p>
+            <span class="hint">支持多张图片,自动分析色调和材质</span>
+          </div>
+        </div>
+        <input #imageInput type="file" multiple accept="image/*" (change)="onFileSelected($event, 'image')" style="display: none;">
+      </div>
+    </div>
+
+    <!-- CAD图纸上传卡片 -->
+    <div class="upload-card cad-card">
+      <div class="card-header">
+        <div class="card-icon cad-icon">
+          <svg width="20" height="20" 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>
+        </div>
+        <h5>CAD图纸</h5>
+      </div>
+      <div class="card-body">
+        <div class="file-upload-zone" (click)="cadInput.click()">
+          <div class="upload-content">
+            <div class="upload-icon">
+              <svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
+                <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
+                <polyline points="7,10 12,15 17,10"></polyline>
+                <line x1="12" y1="15" x2="12" y2="3"></line>
+              </svg>
+            </div>
+            <p>点击上传CAD图纸</p>
+            <span class="hint">自动识别结构和空间尺寸</span>
+          </div>
+        </div>
+        <input #cadInput type="file" accept=".dwg,.dxf,.pdf" (change)="onFileSelected($event, 'cad')" style="display: none;">
+      </div>
+    </div>
+  </div>
+</div>

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

@@ -0,0 +1,256 @@
+// 备选方案:美观的卡片式布局样式
+.materials-section.alternative-layout {
+  // 文本输入区域 - 独立一行
+  .text-upload-section {
+    margin-bottom: 2rem;
+    
+    .upload-item.text-item {
+      background: white;
+      border: 1px solid #e5e5e7;
+      border-radius: 12px;
+      padding: 1.5rem;
+      box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
+      
+      h5 {
+        margin: 0 0 1rem 0;
+        font-size: 1rem;
+        font-weight: 600;
+        color: #1d1d1f;
+        display: flex;
+        align-items: center;
+        gap: 0.5rem;
+        
+        &::before {
+          content: "📝";
+          font-size: 1.2rem;
+        }
+      }
+      
+      textarea {
+        width: 100%;
+        padding: 1rem;
+        border: 1px solid #d2d2d7;
+        border-radius: 8px;
+        font-size: 0.9rem;
+        resize: vertical;
+        margin-bottom: 1rem;
+        font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
+        line-height: 1.5;
+        
+        &:focus {
+          outline: none;
+          border-color: #007aff;
+          box-shadow: 0 0 0 3px rgba(0, 122, 255, 0.1);
+        }
+        
+        &::placeholder {
+          color: #86868b;
+        }
+      }
+      
+      .btn-primary {
+        background: linear-gradient(135deg, #007aff 0%, #0051d5 100%);
+        border: none;
+        border-radius: 8px;
+        padding: 0.75rem 1.5rem;
+        color: white;
+        font-weight: 500;
+        font-size: 0.9rem;
+        cursor: pointer;
+        transition: all 0.2s ease;
+        
+        &:hover:not(:disabled) {
+          transform: translateY(-1px);
+          box-shadow: 0 4px 12px rgba(0, 122, 255, 0.3);
+        }
+        
+        &:disabled {
+          opacity: 0.5;
+          cursor: not-allowed;
+        }
+      }
+    }
+  }
+
+  // 参考图片和CAD图纸 - 美观卡片式并排布局
+  .file-upload-cards {
+    display: grid;
+    grid-template-columns: 1fr 1fr;
+    gap: 1.5rem;
+    
+    // 移动端响应式
+    @media (max-width: 768px) {
+      grid-template-columns: 1fr;
+      gap: 1rem;
+    }
+    
+    .upload-card {
+      background: white;
+      border: 1px solid #e5e5e7;
+      border-radius: 16px;
+      overflow: hidden;
+      box-shadow: 0 4px 16px rgba(0, 0, 0, 0.06);
+      transition: all 0.3s ease;
+      
+      &:hover {
+        transform: translateY(-2px);
+        box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
+      }
+      
+      .card-header {
+        padding: 1.25rem 1.5rem 1rem 1.5rem;
+        border-bottom: 1px solid #f2f2f7;
+        display: flex;
+        align-items: center;
+        gap: 0.75rem;
+        
+        .card-icon {
+          width: 40px;
+          height: 40px;
+          border-radius: 10px;
+          display: flex;
+          align-items: center;
+          justify-content: center;
+          
+          &.image-icon {
+            background: linear-gradient(135deg, #ff9500 0%, #ff6b00 100%);
+            color: white;
+          }
+          
+          &.cad-icon {
+            background: linear-gradient(135deg, #007aff 0%, #0051d5 100%);
+            color: white;
+          }
+        }
+        
+        h5 {
+          margin: 0;
+          font-size: 1.1rem;
+          font-weight: 600;
+          color: #1d1d1f;
+        }
+      }
+      
+      .card-body {
+        padding: 1.5rem;
+        
+        .file-upload-zone {
+          border: 2px dashed #d2d2d7;
+          border-radius: 12px;
+          padding: 2rem 1rem;
+          text-align: center;
+          cursor: pointer;
+          transition: all 0.3s ease;
+          background: #fafafa;
+          min-height: 140px;
+          display: flex;
+          align-items: center;
+          justify-content: center;
+          
+          &:hover {
+            border-color: #007aff;
+            background: rgba(0, 122, 255, 0.02);
+            transform: scale(1.02);
+          }
+          
+          .upload-content {
+            .upload-icon {
+              margin-bottom: 1rem;
+              
+              svg {
+                color: #86868b;
+                transition: color 0.2s ease;
+              }
+            }
+            
+            p {
+              margin: 0 0 0.5rem 0;
+              font-size: 1rem;
+              color: #1d1d1f;
+              font-weight: 500;
+            }
+            
+            .hint {
+              font-size: 0.85rem;
+              color: #86868b;
+              line-height: 1.4;
+            }
+          }
+          
+          &:hover .upload-content {
+            .upload-icon svg {
+              color: #007aff;
+            }
+          }
+        }
+      }
+      
+      // 特定卡片样式
+      &.image-card {
+        .file-upload-zone:hover {
+          border-color: #ff9500;
+          background: rgba(255, 149, 0, 0.02);
+          
+          .upload-content .upload-icon svg {
+            color: #ff9500;
+          }
+        }
+      }
+      
+      &.cad-card {
+        .file-upload-zone:hover {
+          border-color: #007aff;
+          background: rgba(0, 122, 255, 0.02);
+          
+          .upload-content .upload-icon svg {
+            color: #007aff;
+          }
+        }
+      }
+    }
+  }
+}
+
+// 深色模式支持
+@media (prefers-color-scheme: dark) {
+  .materials-section.alternative-layout {
+    .text-upload-section .upload-item.text-item,
+    .file-upload-cards .upload-card {
+      background: #1c1c1e;
+      border-color: #38383a;
+      
+      h5 {
+        color: #f2f2f7;
+      }
+      
+      textarea {
+        background: #2c2c2e;
+        border-color: #48484a;
+        color: #f2f2f7;
+        
+        &::placeholder {
+          color: #8e8e93;
+        }
+      }
+      
+      .file-upload-zone {
+        background: #2c2c2e;
+        border-color: #48484a;
+        
+        .upload-content {
+          p {
+            color: #f2f2f7;
+          }
+          
+          .hint {
+            color: #8e8e93;
+          }
+          
+          .upload-icon svg {
+            color: #8e8e93;
+          }
+        }
+      }
+    }
+  }
+}

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

@@ -0,0 +1,765 @@
+<div class="info-card requirements-confirm-card">
+  <div class="card-header">
+    <h4>确认需求</h4>
+    <div class="header-actions">
+      <button class="btn-ghost btn-sm" (click)="refreshProgress()">刷新进度</button>
+      
+      <!-- 紧凑型流程进度卡片 -->
+      <div class="compact-stage-indicators">
+        <div class="stage-chain">
+          <div class="stage-dot" [class]="getStageStatusClass('materialAnalysis')" 
+               title="素材分析 - {{ getStageStatusText('materialAnalysis') }}">
+            <span class="stage-number">1</span>
+          </div>
+          <div class="stage-connector" [class]="stageCompletionStatus.materialAnalysis ? 'completed' : 'pending'"></div>
+          
+          <div class="stage-dot" [class]="getStageStatusClass('requirementMapping')" 
+               title="需求映射 - {{ getStageStatusText('requirementMapping') }}">
+            <span class="stage-number">2</span>
+          </div>
+          <div class="stage-connector" [class]="stageCompletionStatus.requirementMapping ? 'completed' : 'pending'"></div>
+          
+          <div class="stage-dot" [class]="getStageStatusClass('collaboration')" 
+               title="协作验证 - {{ getStageStatusText('collaboration') }}">
+            <span class="stage-number">3</span>
+          </div>
+          <div class="stage-connector" [class]="stageCompletionStatus.collaboration ? 'completed' : 'pending'"></div>
+          
+          <div class="stage-dot" [class]="getStageStatusClass('progressReview')" 
+               title="进度审查 - {{ getStageStatusText('progressReview') }}">
+            <span class="stage-number">4</span>
+          </div>
+        </div>
+      </div>
+      
+      <div class="progress-indicator">
+        <div class="progress-bar">
+          <div class="progress-fill" [style.width.%]="getProgressPercentage()"></div>
+        </div>
+        <span class="progress-text">{{ getProgressPercentage() | number:'1.0-0' }}% 完成</span>
+      </div>
+    </div>
+  </div>
+
+  <!-- 标签页导航 -->
+  <div class="tab-navigation">
+    <button 
+      class="tab-button" 
+      [class.active]="activeTab === 'materials'"
+      (click)="switchTab('materials')">
+      <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
+        <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path>
+        <polyline points="14,2 14,8 20,8"></polyline>
+      </svg>
+      素材解析
+    </button>
+    <button 
+      class="tab-button" 
+      [class.active]="activeTab === 'mapping'"
+      (click)="switchTab('mapping')">
+      <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
+        <circle cx="12" cy="12" r="3"></circle>
+        <path d="M12 1v6m0 6v6m11-7h-6m-6 0H1"></path>
+      </svg>
+      需求映射
+    </button>
+    <button 
+      class="tab-button" 
+      [class.active]="activeTab === 'collaboration'"
+      (click)="switchTab('collaboration')">
+      <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
+        <path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path>
+        <circle cx="9" cy="7" r="4"></circle>
+        <path d="M23 21v-2a4 4 0 0 0-3-3.87m-4-12a4 4 0 0 1 0 7.75"></path>
+      </svg>
+      协作验证
+    </button>
+    <button 
+      class="tab-button" 
+      [class.active]="activeTab === 'progress'"
+      (click)="switchTab('progress')">
+      <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
+        <polyline points="22,12 18,12 15,21 9,3 6,12 2,12"></polyline>
+      </svg>
+      进度管理
+    </button>
+  </div>
+
+  <!-- 标签页内容 -->
+  <div class="tab-content">
+    
+    <!-- 素材解析标签页 -->
+    @if (activeTab === 'materials') {
+      <div class="materials-section">
+        <!-- 文本输入区域 - 独立一行 -->
+        <div class="text-upload-section">
+          <div class="upload-item text-item">
+            <h5>文本描述</h5>
+            <form [formGroup]="materialUploadForm" (ngSubmit)="onTextSubmit()">
+              <textarea 
+                formControlName="textContent" 
+                placeholder="请描述您的装修需求,如:希望温馨的暖木色调,适合亲子家庭,需要瑜伽区收纳..."
+                rows="4">
+              </textarea>
+              <button type="submit" class="btn-primary btn-sm" [disabled]="!materialUploadForm.get('textContent')?.value">
+                解析文本
+              </button>
+            </form>
+          </div>
+        </div>
+
+        <!-- 参考图片和CAD图纸并排布局 -->
+        <div class="file-upload-grid">
+          <!-- 参考图片上传区域 -->
+          <div class="upload-item image-item">
+            <h5>参考图片</h5>
+            <div class="file-upload-zone" (click)="imageInput.click()">
+              <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
+                <rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect>
+                <circle cx="8.5" cy="8.5" r="1.5"></circle>
+                <polyline points="21,15 16,10 5,21"></polyline>
+              </svg>
+              <p>点击上传参考图片</p>
+              <span class="hint">支持多张图片,自动分析色调和材质</span>
+            </div>
+            <input #imageInput type="file" multiple accept="image/*" (change)="onFileSelected($event, 'image')" style="display: none;">
+          </div>
+
+          <!-- CAD图纸上传区域 -->
+          <div class="upload-item cad-item">
+            <h5>CAD图纸</h5>
+            <div class="file-upload-zone" (click)="cadInput.click()">
+              <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
+                <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path>
+                <polyline points="14,2 14,8 20,8"></polyline>
+                <line x1="16" y1="13" x2="8" y2="13"></line>
+                <line x1="16" y1="17" x2="8" y2="17"></line>
+              </svg>
+              <p>点击上传CAD图纸</p>
+              <span class="hint">自动识别结构和空间尺寸</span>
+            </div>
+            <input #cadInput type="file" accept=".dwg,.dxf,.pdf" (change)="onFileSelected($event, 'cad')" style="display: none;">
+          </div>
+        </div>
+
+        <!-- 已上传素材列表 -->
+        @if (materials.length > 0) {
+          <div class="materials-list">
+            <h5>已上传素材</h5>
+            <div class="material-cards">
+              @for (material of materials; track material.id) {
+                <div class="material-card" [class]="'material-' + material.type">
+                  <div class="material-header">
+                    <span class="material-type">{{ material.type === 'text' ? '文本' : material.type === 'image' ? '图片' : 'CAD' }}</span>
+                    <button class="btn-ghost btn-xs" (click)="removeMaterial(material.id)">
+                      <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
+                        <line x1="18" y1="6" x2="6" y2="18"></line>
+                        <line x1="6" y1="6" x2="18" y2="18"></line>
+                      </svg>
+                    </button>
+                  </div>
+                  <div class="material-name">{{ material.name }}</div>
+                  @if (material.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" 
+                    [(ngModel)]="colorIndicators.mainColor.r"
+                    (ngModelChange)="onSliderChange('r', $event)">
+                  <input 
+                    type="number" 
+                    class="slider-input"
+                    [min]="indicatorRanges.color.r.min" 
+                    [max]="indicatorRanges.color.r.max" 
+                    [(ngModel)]="colorIndicators.mainColor.r"
+                    (ngModelChange)="onInputChange('r', $event)"
+                    (blur)="validateInput('r', $event.target.value)">
+                </div>
+                <div class="rgb-slider">
+                  <span>G</span>
+                  <input 
+                    type="range" 
+                    [min]="indicatorRanges.color.g.min" 
+                    [max]="indicatorRanges.color.g.max" 
+                    [(ngModel)]="colorIndicators.mainColor.g"
+                    (ngModelChange)="onSliderChange('g', $event)">
+                  <input 
+                    type="number" 
+                    class="slider-input"
+                    [min]="indicatorRanges.color.g.min" 
+                    [max]="indicatorRanges.color.g.max" 
+                    [(ngModel)]="colorIndicators.mainColor.g"
+                    (ngModelChange)="onInputChange('g', $event)"
+                    (blur)="validateInput('g', $event.target.value)">
+                </div>
+                <div class="rgb-slider">
+                  <span>B</span>
+                  <input 
+                    type="range" 
+                    [min]="indicatorRanges.color.b.min" 
+                    [max]="indicatorRanges.color.b.max" 
+                    [(ngModel)]="colorIndicators.mainColor.b"
+                    (ngModelChange)="onSliderChange('b', $event)">
+                  <input 
+                    type="number" 
+                    class="slider-input"
+                    [min]="indicatorRanges.color.b.min" 
+                    [max]="indicatorRanges.color.b.max" 
+                    [(ngModel)]="colorIndicators.mainColor.b"
+                    (ngModelChange)="onInputChange('b', $event)"
+                    (blur)="validateInput('b', $event.target.value)">
+                </div>
+                <div class="color-preview" [style.background-color]="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)="onSliderChange('colorTemperature', +$event.target.value)">
+                <input 
+                  type="number" 
+                  class="slider-input"
+                  [min]="indicatorRanges.color.temperature.min" 
+                  [max]="indicatorRanges.color.temperature.max" 
+                  [value]="colorIndicators.colorTemperature"
+                  (input)="onInputChange('colorTemperature', +$event.target.value)"
+                  (blur)="validateInput('colorTemperature', $event.target.value)">
+                <span class="unit-label">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)="onSliderChange('saturation', +$event.target.value)">
+                <input 
+                  type="number" 
+                  class="slider-input"
+                  [min]="indicatorRanges.color.saturation.min" 
+                  [max]="indicatorRanges.color.saturation.max" 
+                  [value]="colorIndicators.saturation"
+                  (input)="onInputChange('saturation', +$event.target.value)"
+                  (blur)="validateInput('saturation', $event.target.value)">
+                <span class="unit-label">%</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)="onSliderChange('blankRatio', +$event.target.value)">
+                <input 
+                  type="number" 
+                  class="slider-input"
+                  [min]="indicatorRanges.space.blankRatio.min" 
+                  [max]="indicatorRanges.space.blankRatio.max" 
+                  [value]="spaceIndicators.blankRatio"
+                  (input)="onInputChange('blankRatio', +$event.target.value)"
+                  (blur)="validateInput('blankRatio', $event.target.value)">
+                <span class="unit-label">%</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)="onSliderChange('lineRatio', +$event.target.value)">
+                <input 
+                  type="number" 
+                  class="slider-input"
+                  [min]="indicatorRanges.space.lineRatio.min" 
+                  [max]="indicatorRanges.space.lineRatio.max" 
+                  [value]="spaceIndicators.lineRatio"
+                  (input)="onInputChange('lineRatio', +$event.target.value)"
+                  (blur)="validateInput('lineRatio', $event.target.value)">
+                <span class="unit-label">%</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)="onSliderChange('flowWidth', +$event.target.value)">
+                <input 
+                  type="number" 
+                  class="slider-input"
+                  [min]="indicatorRanges.space.flowWidth.min" 
+                  [max]="indicatorRanges.space.flowWidth.max" 
+                  step="0.1"
+                  [value]="spaceIndicators.flowWidth"
+                  (input)="onInputChange('flowWidth', +$event.target.value)"
+                  (blur)="validateInput('flowWidth', $event.target.value)">
+                <span class="unit-label">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)="onSliderChange('fabricRatio', +$event.target.value)">
+                <input 
+                  type="number" 
+                  class="slider-input"
+                  [min]="indicatorRanges.material.fabricRatio.min" 
+                  [max]="indicatorRanges.material.fabricRatio.max" 
+                  [value]="materialIndicators.fabricRatio"
+                  (input)="onInputChange('fabricRatio', +$event.target.value)"
+                  (blur)="validateInput('fabricRatio', $event.target.value)">
+                <span class="unit-label">%</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)="onSliderChange('woodRatio', +$event.target.value)">
+                <input 
+                  type="number" 
+                  class="slider-input"
+                  [min]="indicatorRanges.material.woodRatio.min" 
+                  [max]="indicatorRanges.material.woodRatio.max" 
+                  [value]="materialIndicators.woodRatio"
+                  (input)="onInputChange('woodRatio', +$event.target.value)"
+                  (blur)="validateInput('woodRatio', $event.target.value)">
+                <span class="unit-label">%</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)="onSliderChange('metalRatio', +$event.target.value)">
+                <input 
+                  type="number" 
+                  class="slider-input"
+                  [min]="indicatorRanges.material.metalRatio.min" 
+                  [max]="indicatorRanges.material.metalRatio.max" 
+                  [value]="materialIndicators.metalRatio"
+                  (input)="onInputChange('metalRatio', +$event.target.value)"
+                  (blur)="validateInput('metalRatio', $event.target.value)">
+                <span class="unit-label">%</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)="onSliderChange('smoothness', +$event.target.value)">
+                <input 
+                  type="number" 
+                  class="slider-input"
+                  [min]="indicatorRanges.material.smoothness.min" 
+                  [max]="indicatorRanges.material.smoothness.max" 
+                  [value]="materialIndicators.smoothness"
+                  (input)="onInputChange('smoothness', +$event.target.value)"
+                  (blur)="validateInput('smoothness', $event.target.value)">
+                <span class="unit-label">/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="overall-progress">
+          <div class="section-header">
+            <h5>整体进度</h5>
+          </div>
+          
+          <div class="progress-container">
+            <div class="progress-visual">
+              <div class="linear-progress">
+                <div class="progress-track">
+                  <div class="progress-bar-fill" [style.width.%]="getProgressPercentage()"></div>
+                </div>
+                <div class="progress-text">{{ getProgressPercentage() }}%</div>
+              </div>
+              
+              <div class="circular-progress">
+                <svg viewBox="0 0 120 120" class="progress-circle">
+                  <circle cx="60" cy="60" r="54" fill="none" stroke="#e5e7eb" stroke-width="8"></circle>
+                  <circle cx="60" cy="60" r="54" fill="none" stroke="#3b82f6" stroke-width="8"
+                    stroke-linecap="round"
+                    [attr.stroke-dasharray]="339.292"
+                    [attr.stroke-dashoffset]="339.292 - (339.292 * getProgressPercentage() / 100)"
+                    transform="rotate(-90 60 60)">
+                  </circle>
+                </svg>
+                <div class="progress-text">
+                  <div class="progress-percentage">{{ getProgressPercentage() }}%</div>
+                  <div class="progress-label">完成度</div>
+                </div>
+              </div>
+            </div>
+          </div>
+        </div>
+
+        <!-- 进度统计 -->
+        <div class="progress-stats">
+          <div class="stat-item">
+              <div class="stat-number">{{ getRequirementCountByStatus('confirmed') }}</div>
+              <div class="stat-label">已确认</div>
+            </div>
+            <div class="stat-item">
+              <div class="stat-number">{{ getRequirementCountByStatus('pending') }}</div>
+              <div class="stat-label">待确认</div>
+            </div>
+            <div class="stat-item">
+              <div class="stat-number">{{ getRequirementCountByStatus('rejected') }}</div>
+              <div class="stat-label">已拒绝</div>
+            </div>
+          <div class="stat-item">
+            <div class="stat-number">{{ getUnresolvedCommentsCount() }}</div>
+            <div class="stat-label">待解决评论</div>
+          </div>
+        </div>
+
+        <!-- 历史状态 -->
+        <div class="history-section">
+          <div class="section-header">
+            <h5>历史状态</h5>
+            <div class="history-controls">
+              <button class="btn-ghost btn-sm" (click)="saveCurrentState()">保存当前状态</button>
+              <select class="history-select" (change)="onHistoryStateChange($event)">
+                <option value="-1">选择历史状态</option>
+                @for (state of historyStates; track $index) {
+                  <option [value]="$index">{{ state.timestamp | date:'MM-dd HH:mm' }} - {{ state.author }}</option>
+                }
+              </select>
+            </div>
+          </div>
+
+          @if (historyStates.length > 0) {
+            <div class="history-timeline">
+              @for (state of historyStates; track $index) {
+                <div class="timeline-item" (click)="restoreHistoryState($index)">
+                  <div class="timeline-marker"></div>
+                  <div class="timeline-content">
+                    <div class="timeline-header">
+                      <span class="timeline-time">{{ state.timestamp | date:'MM-dd HH:mm' }}</span>
+                      <span class="timeline-author">{{ state.author }}</span>
+                    </div>
+                    <div class="timeline-summary">
+                      状态快照:{{ state.requirementItems.length }}个需求项,
+                      {{ getRequirementCountByStatus('confirmed') }}个已确认
+                    </div>
+                  </div>
+                </div>
+              }
+            </div>
+          } @else {
+            <div class="empty-history">
+              <p>暂无历史状态记录</p>
+              <button class="btn-primary btn-sm" (click)="saveCurrentState()">保存当前状态</button>
+            </div>
+          }
+        </div>
+
+        <!-- 需求状态分布 -->
+        <div class="status-distribution">
+          <h5>需求状态分布</h5>
+          <div class="status-bars">
+            <div class="status-bar">
+              <div class="status-info">
+                <span class="status-name">已确认</span>
+                <span class="status-count">{{ getRequirementCountByStatus('confirmed') }}</span>
+              </div>
+              <div class="status-progress">
+                <div class="progress-bar confirmed" 
+                     [style.width.%]="getStatusPercentage('confirmed')"></div>
+              </div>
+            </div>
+            
+            <div class="status-bar">
+              <div class="status-info">
+                <span class="status-name">待确认</span>
+                <span class="status-count">{{ getRequirementCountByStatus('pending') }}</span>
+              </div>
+              <div class="status-progress">
+                <div class="progress-bar pending" 
+                     [style.width.%]="getStatusPercentage('pending')"></div>
+              </div>
+            </div>
+            
+            <div class="status-bar">
+              <div class="status-info">
+                <span class="status-name">已拒绝</span>
+                <span class="status-count">{{ getRequirementCountByStatus('rejected') }}</span>
+              </div>
+              <div class="status-progress">
+                <div class="progress-bar rejected" 
+                     [style.width.%]="getStatusPercentage('rejected')"></div>
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+    }
+  </div>
+</div>
+
+<!-- 保存状态和手动保存按钮 -->
+<div class="save-section">
+  <div class="save-status">
+    <span class="save-icon" [class]="'save-icon-' + saveStatus">{{ getSaveStatusIcon() }}</span>
+    <span class="save-text">{{ getSaveStatusText() }}</span>
+  </div>
+  
+  <div class="save-actions">
+    <button class="btn-secondary" 
+            [disabled]="isSaving || !hasUnsavedChanges"
+            (click)="manualSave()">
+      @if (isSaving) {
+        <span class="loading-spinner"></span>
+        保存中...
+      } @else {
+        手动保存
+      }
+    </button>
+    
+    <div class="auto-save-toggle">
+      <label class="toggle-label">
+        <input type="checkbox" 
+               [(ngModel)]="autoSaveEnabled"
+               class="toggle-input">
+        <span class="toggle-slider"></span>
+        <span class="toggle-text">自动保存</span>
+      </label>
+    </div>
+  </div>
+</div>

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

@@ -0,0 +1,1671 @@
+@use '../../styles/_ios-theme.scss' as *;
+
+:host { 
+  display: block; 
+  height: 100%; 
+}
+
+// 新增:文本输入区域独立样式
+.text-upload-section {
+  margin-bottom: 1.5rem;
+  
+  .upload-item {
+    background: white;
+    border: 1px solid #e0e0e0;
+    border-radius: 8px;
+    padding: 1rem;
+    
+    h5 {
+      margin: 0 0 0.75rem 0;
+      font-size: 0.9rem;
+      color: #333;
+      font-weight: 600;
+    }
+    
+    textarea {
+      width: 100%;
+      padding: 0.75rem;
+      border: 1px solid #ddd;
+      border-radius: 4px;
+      font-size: 0.85rem;
+      resize: vertical;
+      margin-bottom: 0.75rem;
+      
+      &:focus {
+        outline: none;
+        border-color: #007bff;
+      }
+    }
+    
+    .btn-primary {
+      background: #007bff;
+      border: none;
+      border-radius: 4px;
+      padding: 0.5rem 1rem;
+      color: white;
+      font-size: 0.85rem;
+      cursor: pointer;
+      
+      &:disabled {
+        opacity: 0.6;
+        cursor: not-allowed;
+      }
+    }
+  }
+}
+
+// 新增:参考图片和CAD图纸并排布局
+.file-upload-grid {
+  display: grid;
+  grid-template-columns: 1fr 1fr;
+  gap: 1rem;
+  
+  // 移动端响应式
+  @media (max-width: 768px) {
+    grid-template-columns: 1fr;
+  }
+}
+
+// 导入备选方案样式
+@import './requirements-confirm-card-alternative.scss';
+
+.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;
+    }
+    
+    .header-actions {
+      display: flex;
+      align-items: center;
+      gap: $ios-spacing-md;
+      
+      .btn-ghost {
+        white-space: nowrap;
+      }
+    }
+    
+    .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;
+      }
+    }
+  }
+
+  // 滑动条输入框样式
+  .slider-container {
+    display: flex;
+    align-items: center;
+    gap: $ios-spacing-xs;
+    
+    input[type="range"] {
+      flex: 1;
+    }
+    
+    .slider-input {
+      width: 60px;
+      padding: 4px 8px;
+      border: 1px solid $ios-border;
+      border-radius: 4px;
+      font-size: $ios-font-size-xs;
+      text-align: center;
+      background: white;
+      
+      &:focus {
+        outline: none;
+        border-color: $ios-primary;
+        box-shadow: 0 0 0 2px rgba(0, 122, 255, 0.2);
+      }
+    }
+    
+    .unit-label {
+      font-size: $ios-font-size-xs;
+      color: $ios-text-secondary;
+      min-width: 20px;
+    }
+  }
+
+  // 进度条样式
+  .progress-container {
+    margin-bottom: $ios-spacing-md;
+    
+    &.progress-updated {
+      .progress-bar-fill {
+        animation: progressPulse 0.5s ease;
+      }
+    }
+    
+    .progress-visual {
+      display: flex;
+      align-items: center;
+      gap: $ios-spacing-md;
+      
+      .linear-progress {
+        flex: 1;
+        display: flex;
+        align-items: center;
+        gap: $ios-spacing-sm;
+        
+        .progress-track {
+          flex: 1;
+          height: 8px;
+          background: $ios-background-secondary;
+          border-radius: 4px;
+          overflow: hidden;
+          
+          .progress-bar-fill {
+            height: 100%;
+            background: linear-gradient(90deg, #007AFF 0%, #34C759 100%);
+            border-radius: 4px;
+            transition: width 0.3s ease;
+          }
+        }
+        
+        .progress-text {
+          font-size: $ios-font-size-sm;
+          font-weight: $ios-font-weight-semibold;
+          color: $ios-text-primary;
+          min-width: 40px;
+        }
+      }
+      
+      .circular-progress {
+        position: relative;
+        width: 120px;
+        height: 120px;
+        
+        .progress-circle {
+          width: 100%;
+          height: 100%;
+          transform: rotate(-90deg);
+          
+          circle {
+            transition: stroke-dashoffset 0.3s ease;
+          }
+        }
+        
+        .progress-text {
+          position: absolute;
+          top: 50%;
+          left: 50%;
+          transform: translate(-50%, -50%);
+          text-align: center;
+          
+          .progress-percentage {
+            font-size: $ios-font-size-lg;
+            font-weight: $ios-font-weight-bold;
+            color: $ios-text-primary;
+          }
+          
+          .progress-label {
+            font-size: $ios-font-size-xs;
+            color: $ios-text-secondary;
+          }
+        }
+      }
+    }
+  }
+
+  // 进度统计样式
+  .progress-stats {
+    display: grid;
+    grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
+    gap: $ios-spacing-sm;
+    margin-bottom: $ios-spacing-md;
+    
+    .stat-item {
+      text-align: center;
+      padding: $ios-spacing-sm;
+      background: $ios-background-secondary;
+      border-radius: 8px;
+      
+      .stat-number {
+        font-size: $ios-font-size-lg;
+        font-weight: $ios-font-weight-bold;
+        color: $ios-text-primary;
+      }
+      
+      .stat-label {
+        font-size: $ios-font-size-xs;
+        color: $ios-text-secondary;
+        margin-top: 2px;
+      }
+    }
+  }
+
+  // 标签页导航
+  .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 {
+    // 文本输入区域 - 独立一行
+    .text-upload-section {
+      margin-bottom: $ios-spacing-lg;
+      
+      .upload-item.text-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);
+          }
+        }
+      }
+    }
+
+    // 参考图片和CAD图纸并排布局
+    .file-upload-grid {
+      display: grid;
+      grid-template-columns: 1fr 1fr;
+      gap: 2%;
+      margin-bottom: $ios-spacing-lg;
+      
+      // 移动端响应式 - 改为垂直布局
+      @media (max-width: 768px) {
+        grid-template-columns: 1fr;
+        gap: $ios-spacing-md;
+      }
+      
+      .upload-item {
+        &.image-item, &.cad-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;
+          }
+          
+          .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;
+            min-height: 120px;
+            display: flex;
+            flex-direction: column;
+            justify-content: center;
+            align-items: center;
+            
+            &: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;
+            }
+          }
+        }
+      }
+    }
+
+    // 保留原有的upload-grid样式作为备选方案
+    .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;
+              transition: background-color 0.1s ease; // 减少过渡时间,提高响应速度
+              // 移除默认背景色,完全依赖Angular绑定
+              will-change: background-color; // 优化GPU渲染
+            }
+          }
+        }
+      }
+    }
+
+    // 预设氛围样式
+    .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;
+    }
+  }
+}
+
+// 进度动画
+@keyframes progressPulse {
+  0% { opacity: 0.6; }
+  50% { opacity: 1; }
+  100% { opacity: 0.6; }
+}
+
+// 全局通知样式
+.execution-notification {
+  position: fixed;
+  top: 20px;
+  right: 20px;
+  background: #4CAF50;
+  color: white;
+  padding: 16px 24px;
+  border-radius: 8px;
+  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
+  z-index: 1000;
+  animation: slideInRight 0.3s ease-out;
+  
+  &.error {
+    background: #f44336;
+  }
+  
+  &.warning {
+    background: #ff9800;
+  }
+}
+
+@keyframes slideInRight {
+  from {
+    transform: translateX(100%);
+    opacity: 0;
+  }
+  to {
+    transform: translateX(0);
+    opacity: 1;
+  }
+}
+
+// 紧凑型流程进度指示器
+.compact-stage-indicators {
+  display: flex;
+  align-items: center;
+  margin: 0 16px;
+  
+  .stage-chain {
+    display: flex;
+    align-items: center;
+    gap: 8px;
+  }
+  
+  .stage-dot {
+    width: 28px;
+    height: 28px;
+    border-radius: 50%;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    font-size: 12px;
+    font-weight: 600;
+    color: white;
+    cursor: pointer;
+    transition: all 0.3s ease;
+    position: relative;
+    
+    .stage-number {
+      font-size: 11px;
+      font-weight: bold;
+    }
+    
+    // 已完成状态 - 绿色
+    &.stage-completed {
+      background: linear-gradient(135deg, #10b981, #059669);
+      box-shadow: 0 2px 8px rgba(16, 185, 129, 0.3);
+      
+      &:hover {
+        transform: scale(1.1);
+        box-shadow: 0 4px 12px rgba(16, 185, 129, 0.4);
+      }
+    }
+    
+    // 进行中状态 - 红色
+    &.stage-in-progress {
+      background: linear-gradient(135deg, #ef4444, #dc2626);
+      box-shadow: 0 2px 8px rgba(239, 68, 68, 0.3);
+      animation: pulse-red 2s infinite;
+      
+      &:hover {
+        transform: scale(1.1);
+        box-shadow: 0 4px 12px rgba(239, 68, 68, 0.4);
+      }
+    }
+    
+    // 未进行状态 - 黄色
+    &.stage-pending {
+      background: linear-gradient(135deg, #f59e0b, #d97706);
+      box-shadow: 0 2px 8px rgba(245, 158, 11, 0.3);
+      
+      &:hover {
+        transform: scale(1.1);
+        box-shadow: 0 4px 12px rgba(245, 158, 11, 0.4);
+      }
+    }
+  }
+  
+  .stage-connector {
+    width: 20px;
+    height: 3px;
+    border-radius: 2px;
+    transition: all 0.3s ease;
+    
+    &.completed {
+      background: linear-gradient(90deg, #10b981, #059669);
+    }
+    
+    &.pending {
+      background: #e5e7eb;
+    }
+  }
+}
+
+// 进行中状态的脉冲动画
+@keyframes pulse-red {
+  0%, 100% {
+    box-shadow: 0 2px 8px rgba(239, 68, 68, 0.3);
+  }
+  50% {
+    box-shadow: 0 2px 12px rgba(239, 68, 68, 0.6);
+  }
+}
+
+// 更新header-actions布局
+.header-actions {
+  display: flex;
+  align-items: center;
+  gap: 16px;
+  
+  .btn-ghost {
+    flex-shrink: 0;
+  }
+  
+  .compact-stage-indicators {
+    flex-shrink: 0;
+  }
+  
+  .progress-indicator {
+    flex-shrink: 0;
+  }
+}
+
+// 保存状态区域
+.save-section {
+  margin-top: 24px;
+  padding: 16px;
+  background: #f8fafc;
+  border-radius: 8px;
+  border: 1px solid #e2e8f0;
+  
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  gap: 16px;
+  
+  .save-status {
+    display: flex;
+    align-items: center;
+    gap: 8px;
+    
+    .save-icon {
+      font-size: 16px;
+      font-weight: bold;
+      
+      &.save-icon-saved {
+        color: #10b981;
+      }
+      
+      &.save-icon-saving {
+        color: #3b82f6;
+        animation: spin 1s linear infinite;
+      }
+      
+      &.save-icon-error {
+        color: #ef4444;
+      }
+      
+      &.save-icon-unsaved {
+        color: #f59e0b;
+      }
+    }
+    
+    .save-text {
+      font-size: 14px;
+      color: #64748b;
+    }
+  }
+  
+  .save-actions {
+    display: flex;
+    align-items: center;
+    gap: 16px;
+    
+    .btn-secondary {
+      padding: 8px 16px;
+      background: #3b82f6;
+      color: white;
+      border: none;
+      border-radius: 6px;
+      font-size: 14px;
+      cursor: pointer;
+      transition: all 0.2s ease;
+      
+      &:hover:not(:disabled) {
+        background: #2563eb;
+        transform: translateY(-1px);
+      }
+      
+      &:disabled {
+        background: #94a3b8;
+        cursor: not-allowed;
+        transform: none;
+      }
+      
+      .loading-spinner {
+        display: inline-block;
+        width: 12px;
+        height: 12px;
+        border: 2px solid transparent;
+        border-top: 2px solid currentColor;
+        border-radius: 50%;
+        animation: spin 1s linear infinite;
+        margin-right: 8px;
+      }
+    }
+    
+    .auto-save-toggle {
+      .toggle-label {
+        display: flex;
+        align-items: center;
+        gap: 8px;
+        cursor: pointer;
+        
+        .toggle-input {
+          display: none;
+        }
+        
+        .toggle-slider {
+          width: 40px;
+          height: 20px;
+          background: #cbd5e1;
+          border-radius: 10px;
+          position: relative;
+          transition: all 0.3s ease;
+          
+          &::after {
+            content: '';
+            position: absolute;
+            top: 2px;
+            left: 2px;
+            width: 16px;
+            height: 16px;
+            background: white;
+            border-radius: 50%;
+            transition: all 0.3s ease;
+          }
+        }
+        
+        .toggle-input:checked + .toggle-slider {
+          background: #10b981;
+          
+          &::after {
+            transform: translateX(20px);
+          }
+        }
+        
+        .toggle-text {
+          font-size: 14px;
+          color: #64748b;
+        }
+      }
+    }
+  }
+}
+
+@keyframes spin {
+  from {
+    transform: rotate(0deg);
+  }
+  to {
+    transform: rotate(360deg);
+  }
+}
+
+@keyframes pulse {
+  0%, 100% { opacity: 1; }
+  50% { opacity: 0.5; }
+}

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

@@ -0,0 +1,1406 @@
+import { Component, Input, Output, EventEmitter, OnInit, OnDestroy, ChangeDetectorRef, ChangeDetectionStrategy } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FormsModule, ReactiveFormsModule, FormBuilder, FormGroup, Validators } from '@angular/forms';
+
+// 素材文件接口
+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'],
+  changeDetection: ChangeDetectionStrategy.Default // 确保使用默认变更检测策略
+})
+export class RequirementsConfirmCardComponent implements OnInit, OnDestroy {
+  @Input() projectId?: string;
+  @Output() requirementConfirmed = new EventEmitter<RequirementItem>();
+  @Output() progressUpdated = new EventEmitter<number>();
+  @Output() stageCompleted = new EventEmitter<{ stage: string; allStagesCompleted: boolean }>();
+
+  // 表单
+  materialForm!: FormGroup;
+  commentForm!: FormGroup;
+  materialUploadForm!: FormGroup;
+
+  // 数据
+  materialFiles: MaterialFile[] = [];
+  materials: MaterialFile[] = [];
+  requirementMetrics: RequirementMetric[] = [];
+  collaborationComments: CollaborationComment[] = [];
+  requirementItems: RequirementItem[] = [];
+
+  // 状态
+  activeTab: 'materials' | 'mapping' | 'collaboration' | 'progress' = 'materials';
+  isUploading = false;
+  isAnalyzing = false;
+  dragOver = false;
+  showConsistencyWarning = false;
+  warningMessage = '';
+
+  // 自动保存相关状态
+  autoSaveEnabled = true;
+  lastSaveTime?: Date;
+  hasUnsavedChanges = false;
+  isSaving = false;
+  saveStatus: 'saved' | 'saving' | 'error' | 'unsaved' = 'saved';
+
+  // 流程状态
+  stageCompletionStatus = {
+    materialAnalysis: false,
+    requirementMapping: false,
+    collaboration: false,
+    progressReview: false
+  };
+
+  // 自动保存定时器
+  private autoSaveTimer?: any;
+
+  // 需求指标
+  colorIndicators = {
+    mainColor: { r: 255, g: 230, b: 180 },
+    colorTemperature: 2700,
+    colorRange: '暖色调',
+    saturation: 70,
+    brightness: 80,
+    contrast: 60
+  };
+
+  spaceIndicators = {
+    lineRatio: 60,
+    blankRatio: 30,
+    flowWidth: 0.9,
+    aspectRatio: 1.6,
+    ceilingHeight: 2.8,
+    lightingLevel: 300
+  };
+
+  materialIndicators = {
+    fabricRatio: 50,
+    woodRatio: 30,
+    metalRatio: 20,
+    smoothness: 7,
+    glossiness: 4,
+    texture: 6
+  };
+
+  // 指标范围配置
+  indicatorRanges = {
+    color: {
+      r: { min: 0, max: 255 },
+      g: { min: 0, max: 255 },
+      b: { min: 0, max: 255 },
+      temperature: { min: 2000, max: 6500 },
+      saturation: { min: 0, max: 100 },
+      brightness: { min: 0, max: 100 },
+      contrast: { min: 0, max: 100 }
+    },
+    space: {
+      lineRatio: { min: 0, max: 100 },
+      blankRatio: { min: 10, max: 80 },
+      flowWidth: { min: 0.6, max: 1.5 },
+      aspectRatio: { min: 1.0, max: 3.0 },
+      ceilingHeight: { min: 2.4, max: 4.0 },
+      lightingLevel: { min: 100, max: 800 }
+    },
+    material: {
+      fabricRatio: { min: 0, max: 100 },
+      woodRatio: { min: 0, max: 100 },
+      metalRatio: { min: 0, max: 100 },
+      smoothness: { min: 1, max: 10 },
+      glossiness: { min: 1, max: 10 },
+      texture: { min: 1, max: 10 }
+    }
+  };
+
+  // 预设数据
+  presetAtmospheres = [
+    { name: '温馨暖调', rgb: '255,230,180', colorTemp: '2700-3000K', materials: ['木质', '布艺'] },
+    { name: '现代冷调', rgb: '200,220,240', colorTemp: '5000-6000K', materials: ['金属', '玻璃'] },
+    { name: '自然清新', rgb: '220,240,220', colorTemp: '4000-4500K', materials: ['竹木', '石材'] }
+  ];
+
+  // 一致性检查
+  consistencyWarnings: string[] = [];
+  historyStates: any[] = [];
+
+  constructor(private fb: FormBuilder, private cdr: ChangeDetectorRef) {}
+
+  ngOnInit() {
+    this.initializeForms();
+    this.initializeRequirements();
+    this.loadPresetMetrics();
+    
+    // 启用自动保存
+    this.autoSaveEnabled = true;
+    
+    // 定期检查阶段完成状态
+    setInterval(() => {
+      this.checkStageCompletion();
+    }, 5000);
+  }
+
+  ngOnDestroy(): void {
+    // 清理自动保存定时器
+    if (this.autoSaveTimer) {
+      clearTimeout(this.autoSaveTimer);
+    }
+  }
+
+  private initializeForms() {
+    this.materialForm = this.fb.group({
+      textContent: ['', Validators.required],
+      atmosphereDescription: [''],
+      residentInfo: [''],
+      scenePreferences: [''],
+      title: [''],
+      content: ['']
+    });
+
+    this.commentForm = this.fb.group({
+      content: ['', Validators.required],
+      requirementId: ['']
+    });
+
+    this.materialUploadForm = this.fb.group({
+      file: [''],
+      textContent: ['', Validators.required]
+    });
+  }
+
+  private initializeRequirements() {
+    this.requirementItems = [
+      {
+        id: 'color-atmosphere',
+        title: '色彩氛围',
+        description: '整体色调和氛围营造',
+        priority: 'high',
+        status: 'pending',
+        lastUpdated: new Date()
+      },
+      {
+        id: 'space-layout',
+        title: '空间布局',
+        description: '功能分区和动线设计',
+        priority: 'high',
+        status: 'pending',
+        lastUpdated: new Date()
+      },
+      {
+        id: 'material-selection',
+        title: '材质选择',
+        description: '主要材质和质感要求',
+        priority: 'medium',
+        status: 'pending',
+        lastUpdated: new Date()
+      }
+    ];
+  }
+
+  private loadPresetMetrics() {
+    this.requirementMetrics = [
+      {
+        id: 'main-color',
+        category: 'color',
+        name: '主色调',
+        value: { r: 255, g: 230, b: 180 },
+        description: '空间主要色彩'
+      },
+      {
+        id: 'color-temp',
+        category: 'color',
+        name: '色温',
+        value: 3000,
+        unit: 'K',
+        range: { min: 2700, max: 6500 },
+        description: '照明色温范围'
+      },
+      {
+        id: 'wood-ratio',
+        category: 'material',
+        name: '木质占比',
+        value: 60,
+        unit: '%',
+        range: { min: 0, max: 100 },
+        description: '木质材料使用比例'
+      }
+    ];
+  }
+
+  // 素材上传功能
+  onFileSelected(event: Event, type: 'text' | 'image' | 'cad') {
+    const input = event.target as HTMLInputElement;
+    if (input.files) {
+      Array.from(input.files).forEach(file => {
+        this.uploadFile(file, type);
+      });
+    }
+  }
+
+  onFileDrop(event: DragEvent, type: 'text' | 'image' | 'cad') {
+    event.preventDefault();
+    this.dragOver = false;
+    
+    if (event.dataTransfer?.files) {
+      Array.from(event.dataTransfer.files).forEach(file => {
+        this.uploadFile(file, type);
+      });
+    }
+  }
+
+  onDragOver(event: DragEvent) {
+    event.preventDefault();
+    this.dragOver = true;
+  }
+
+  onDragLeave(event: DragEvent) {
+    event.preventDefault();
+    this.dragOver = false;
+  }
+
+  private uploadFile(file: File, type: 'text' | 'image' | 'cad') {
+    this.isUploading = true;
+    
+    // 模拟文件上传
+    setTimeout(() => {
+      const materialFile: MaterialFile = {
+        id: this.generateId(),
+        name: file.name,
+        type: type,
+        url: URL.createObjectURL(file),
+        size: this.formatFileSize(file.size),
+        uploadTime: new Date()
+      };
+
+      this.materialFiles.push(materialFile);
+      this.materials.push(materialFile);
+      this.isUploading = false;
+      
+      // 自动解析
+      this.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) {
+    // 检查是否可以进行需求确认操作
+    if (!this.canProceedToNextStage('materialAnalysis')) {
+      alert('请先完成素材分析阶段的所有必要操作');
+      return;
+    }
+    
+    const requirement = this.requirementItems.find(r => r.id === requirementId);
+    if (requirement) {
+      requirement.status = 'confirmed';
+      requirement.lastUpdated = new Date();
+      this.requirementConfirmed.emit(requirement);
+      this.updateProgress();
+      this.triggerAutoSave();
+      
+      // 检查阶段完成状态
+      this.checkStageCompletion();
+    }
+  }
+
+  rejectRequirement(requirementId: string, reason?: string) {
+    // 检查是否可以进行需求拒绝操作
+    if (!this.canProceedToNextStage('materialAnalysis')) {
+      alert('请先完成素材分析阶段的所有必要操作');
+      return;
+    }
+    
+    const requirement = this.requirementItems.find(r => r.id === requirementId);
+    if (requirement) {
+      requirement.status = 'rejected';
+      requirement.lastUpdated = new Date();
+      
+      if (reason) {
+        this.addCommentToRequirement(requirementId, reason, 'system');
+      }
+      
+      this.updateProgress();
+      this.triggerAutoSave();
+      
+      // 检查阶段完成状态
+      this.checkStageCompletion();
+    }
+  }
+
+  // 协作功能
+  addComment() {
+    if (this.commentForm.valid) {
+      const comment: CollaborationComment = {
+        id: this.generateId(),
+        author: '当前用户',
+        role: 'designer',
+        content: this.commentForm.value.content,
+        timestamp: new Date(),
+        requirementId: this.commentForm.value.requirementId,
+        status: 'pending'
+      };
+      
+      this.collaborationComments.push(comment);
+      this.commentForm.reset();
+      this.triggerAutoSave();
+      
+      // 检查阶段完成状态
+      this.checkStageCompletion();
+    }
+  }
+
+  resolveComment(commentId: string) {
+    const comment = this.collaborationComments.find(c => c.id === commentId);
+    if (comment) {
+      comment.status = 'resolved';
+      this.triggerAutoSave();
+      
+      // 检查阶段完成状态
+      this.checkStageCompletion();
+    }
+  }
+
+  // 进度管理
+  private updateProgress() {
+    const totalRequirements = this.requirementItems.length;
+    const confirmedRequirements = this.requirementItems.filter(r => r.status === 'confirmed').length;
+    const progress = totalRequirements > 0 ? (confirmedRequirements / totalRequirements) * 100 : 0;
+    
+    // 实时更新进度条
+    this.updateProgressBar(progress);
+    this.progressUpdated.emit(progress);
+    
+    // 检查是否完成所有需求
+    if (progress === 100) {
+      this.onRequirementCommunicationComplete();
+    }
+  }
+
+  private updateProgressBar(progress: number): void {
+    // 更新进度条显示
+    const progressBar = document.querySelector('.progress-bar-fill') as HTMLElement;
+    if (progressBar) {
+      progressBar.style.width = `${progress}%`;
+    }
+    
+    // 更新进度文本
+    const progressText = document.querySelector('.progress-text') as HTMLElement;
+    if (progressText) {
+      progressText.textContent = `${Math.round(progress)}%`;
+    }
+    
+    // 添加进度动画效果
+    this.animateProgressUpdate(progress);
+  }
+
+  private animateProgressUpdate(progress: number): void {
+    // 添加进度更新的视觉反馈
+    const progressContainer = document.querySelector('.progress-container') as HTMLElement;
+    if (progressContainer) {
+      progressContainer.classList.add('progress-updated');
+      setTimeout(() => {
+        progressContainer.classList.remove('progress-updated');
+      }, 500);
+    }
+  }
+
+  // 需求沟通阶段完成处理
+  private onRequirementCommunicationComplete(): void {
+    console.log('需求沟通阶段完成,开始同步关键信息');
+    this.syncKeyInfoToSolutionConfirmation();
+  }
+
+  // 同步关键信息到方案确认阶段
+  private syncKeyInfoToSolutionConfirmation(): void {
+    const keyInfo = {
+      colorAtmosphere: {
+        mainColor: this.colorIndicators.mainColor,
+        colorTemperature: this.colorIndicators.colorTemperature,
+        saturation: this.colorIndicators.saturation,
+        brightness: this.colorIndicators.brightness
+      },
+      spaceStructure: {
+        lineRatio: this.spaceIndicators.lineRatio,
+        blankRatio: this.spaceIndicators.blankRatio,
+        flowWidth: this.spaceIndicators.flowWidth
+      },
+      materialWeights: {
+        fabricRatio: this.materialIndicators.fabricRatio,
+        woodRatio: this.materialIndicators.woodRatio,
+        metalRatio: this.materialIndicators.metalRatio,
+        smoothness: this.materialIndicators.smoothness
+      },
+      presetAtmosphere: this.getSelectedPresetAtmosphere()
+    };
+
+    // 触发同步事件
+    this.requirementConfirmed.emit({
+      id: 'requirement-sync',
+      title: '需求沟通完成',
+      description: '关键信息已同步至方案确认阶段',
+      priority: 'high',
+      status: 'confirmed',
+      lastUpdated: new Date(),
+      metrics: this.convertToMetrics(keyInfo)
+    });
+
+    // 启动交付执行流程
+    setTimeout(() => {
+      this.startDeliveryExecution();
+    }, 1000);
+  }
+
+  private getSelectedPresetAtmosphere(): any {
+    // 根据当前颜色指标找到最匹配的预设氛围
+    const currentRgb = `${this.colorIndicators.mainColor.r},${this.colorIndicators.mainColor.g},${this.colorIndicators.mainColor.b}`;
+    return this.presetAtmospheres.find(preset => preset.rgb === currentRgb) || this.presetAtmospheres[0];
+  }
+
+  private convertToMetrics(keyInfo: any): RequirementMetric[] {
+    return [
+      {
+        id: 'color-sync',
+        category: 'color',
+        name: '色彩氛围',
+        value: keyInfo.colorAtmosphere
+      },
+      {
+        id: 'space-sync',
+        category: 'space',
+        name: '空间结构',
+        value: keyInfo.spaceStructure
+      },
+      {
+        id: 'material-sync',
+        category: 'material',
+        name: '材质权重',
+        value: keyInfo.materialWeights
+      }
+    ];
+  }
+
+  // 启动交付执行流程
+  private startDeliveryExecution(): void {
+    console.log('启动交付执行流程');
+    
+    // 显示执行启动提示
+    this.showExecutionStartNotification();
+    
+    // 这里可以触发路由跳转或其他业务逻辑
+    // 例如:this.router.navigate(['/project', this.projectId, 'execution']);
+  }
+
+  private showExecutionStartNotification(): void {
+    // 显示执行流程启动的通知
+    const notification = document.createElement('div');
+    notification.className = 'execution-notification';
+    notification.innerHTML = `
+      <div class="notification-content">
+        <svg viewBox="0 0 24 24" fill="currentColor">
+          <path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"/>
+        </svg>
+        <span>交付执行流程已启动</span>
+      </div>
+    `;
+    
+    document.body.appendChild(notification);
+    
+    setTimeout(() => {
+      notification.remove();
+    }, 3000);
+  }
+
+  getProgressPercentage(): number {
+    if (this.requirementItems.length === 0) return 0;
+    const confirmedCount = this.requirementItems.filter(r => r.status === 'confirmed').length;
+    return Math.round((confirmedCount / this.requirementItems.length) * 100);
+  }
+
+  // 处理优先级更新
+  onPriorityChange(requirementId: string, event: Event): void {
+    const target = event.target as HTMLSelectElement;
+    this.updateRequirementPriority(requirementId, target.value as 'high' | 'medium' | 'low');
+  }
+
+  // 检查需求是否有待处理评论
+  hasUnreadComments(requirementId: string): boolean {
+    return this.getCommentsForRequirement(requirementId).some(c => c.status === 'pending');
+  }
+
+  // 处理历史状态选择
+  onHistoryStateChange(event: Event): void {
+    const target = event.target as HTMLSelectElement;
+    const index = +target.value;
+    if (index >= 0) {
+      this.restoreHistoryState(index);
+    }
+  }
+
+  // 获取指定状态的需求数量
+  getRequirementCountByStatus(status: 'confirmed' | 'pending' | 'rejected'): number {
+    return (this.requirementItems || []).filter(r => r.status === status).length;
+  }
+
+  // 获取状态百分比
+  getStatusPercentage(status: 'confirmed' | 'pending' | 'rejected'): number {
+    const total = this.requirementItems.length;
+    if (total === 0) return 0;
+    
+    const statusCount = this.getRequirementCountByStatus(status);
+    return Math.round((statusCount / total) * 100);
+  }
+
+  // 指标映射功能
+  updateMetricValue(metricId: string, value: any) {
+    const metric = this.requirementMetrics.find(m => m.id === metricId);
+    if (metric) {
+      metric.value = value;
+      this.checkConsistency();
+    }
+  }
+
+  // 滑动条数值同步方法
+  onSliderChange(key: string, value: number): void {
+    console.log(`滑动条变化: ${key} = ${value}`);
+    console.log('变化前的mainColor:', JSON.stringify(this.colorIndicators.mainColor));
+    
+    // 直接更新数据,不需要额外的updateIndicatorValue调用
+    // 因为使用了双向绑定,数据已经自动更新
+    if (key === 'r' || key === 'g' || key === 'b') {
+      // 确保RGB值在有效范围内
+      const range = this.getIndicatorRange(key);
+      if (range) {
+        const clampedValue = Math.max(range.min, Math.min(range.max, value));
+        this.colorIndicators.mainColor[key as keyof typeof this.colorIndicators.mainColor] = clampedValue;
+        console.log(`RGB值已更新: ${key} = ${clampedValue}`);
+        console.log('变化后的mainColor:', JSON.stringify(this.colorIndicators.mainColor));
+      }
+      
+      // 触发颜色指标更新
+      this.updateColorIndicator('mainColor', this.colorIndicators.mainColor);
+    } else {
+      // 处理其他颜色指标
+      this.updateIndicatorValue(key, value);
+    }
+    
+    this.triggerAutoSave(); // 触发自动保存
+    this.checkStageCompletion(); // 检查阶段完成状态
+    
+    // 强制触发变更检测
+    this.cdr.detectChanges();
+    console.log('滑动条变更检测已触发');
+  }
+
+  onInputChange(key: string, value: number): void {
+    console.log(`输入框变化: ${key} = ${value}`);
+    console.log('变化前的mainColor:', JSON.stringify(this.colorIndicators.mainColor));
+    
+    // 直接更新数据,不需要额外的updateIndicatorValue调用
+    // 因为使用了双向绑定,数据已经自动更新
+    if (key === 'r' || key === 'g' || key === 'b') {
+      // 确保RGB值在有效范围内
+      const range = this.getIndicatorRange(key);
+      if (range) {
+        const clampedValue = Math.max(range.min, Math.min(range.max, value));
+        this.colorIndicators.mainColor[key as keyof typeof this.colorIndicators.mainColor] = clampedValue;
+        console.log(`RGB值已更新: ${key} = ${clampedValue}`);
+        console.log('变化后的mainColor:', JSON.stringify(this.colorIndicators.mainColor));
+      }
+      
+      // 触发颜色指标更新
+      this.updateColorIndicator('mainColor', this.colorIndicators.mainColor);
+    } else {
+      // 处理其他颜色指标
+      this.updateIndicatorValue(key, value);
+    }
+    
+    this.triggerAutoSave();
+    
+    // 强制触发变更检测
+    this.cdr.detectChanges();
+    console.log('输入框变更检测已触发');
+  }
+
+  validateInput(key: string, value: string): void {
+    const numValue = parseFloat(value);
+    if (isNaN(numValue)) {
+      // 如果输入无效,恢复原值
+      this.restoreIndicatorValue(key);
+      return;
+    }
+    
+    const range = this.getIndicatorRange(key);
+    if (range) {
+      const clampedValue = Math.max(range.min, Math.min(range.max, numValue));
+      this.updateIndicatorValue(key, clampedValue);
+    }
+  }
+
+  private updateIndicatorValue(key: string, value: number): void {
+    console.log(`updateIndicatorValue调用: ${key} = ${value}`);
+    
+    // 更新颜色指标
+    if (key in this.colorIndicators) {
+      if (key === 'r' || key === 'g' || key === 'b') {
+        // 创建新的mainColor对象以确保变更检测
+        const oldColor = { ...this.colorIndicators.mainColor };
+        this.colorIndicators.mainColor = {
+          ...this.colorIndicators.mainColor,
+          [key]: value
+        };
+        console.log(`RGB更新: ${key}从${oldColor[key as keyof typeof oldColor]}变为${value}`, 
+                   '新mainColor:', this.colorIndicators.mainColor);
+        this.updateColorIndicator('mainColor', this.colorIndicators.mainColor);
+      } else {
+        (this.colorIndicators as any)[key] = value;
+        this.updateColorIndicator(key, value);
+      }
+      return;
+    }
+
+    // 更新空间指标
+    if (key in this.spaceIndicators) {
+      (this.spaceIndicators as any)[key] = value;
+      this.updateSpaceIndicator(key, value);
+      return;
+    }
+
+    // 更新材质指标
+    if (key in this.materialIndicators) {
+      (this.materialIndicators as any)[key] = value;
+      this.updateMaterialIndicator(key, value);
+      return;
+    }
+  }
+
+  private restoreIndicatorValue(key: string): void {
+    // 这里可以从历史状态或默认值恢复
+    console.log(`恢复指标值: ${key}`);
+  }
+
+  private getIndicatorRange(key: string): { min: number; max: number } | null {
+    // 检查颜色范围
+    if (key === 'r' || key === 'g' || key === 'b') {
+      return this.indicatorRanges.color[key as keyof typeof this.indicatorRanges.color];
+    }
+    if (key in this.indicatorRanges.color) {
+      return (this.indicatorRanges.color as any)[key];
+    }
+
+    // 检查空间范围
+    if (key in this.indicatorRanges.space) {
+      return (this.indicatorRanges.space as any)[key];
+    }
+
+    // 检查材质范围
+    if (key in this.indicatorRanges.material) {
+      return (this.indicatorRanges.material as any)[key];
+    }
+
+    return null;
+  }
+
+  // 指标更新方法
+  updateColorIndicator(type: string, value?: any): void {
+    switch (type) {
+      case 'mainColor':
+        if (value && typeof value === 'object' && 'r' in value) {
+          this.colorIndicators.mainColor = { ...value }; // 创建新对象以触发变更检测
+          console.log('mainColor更新后:', this.colorIndicators.mainColor);
+        }
+        break;
+      case 'colorTemperature':
+        // 色温更新时不需要改变颜色值,只是触发一致性检查
+        break;
+      case 'saturation':
+        // 饱和度更新时不需要改变颜色值,只是触发一致性检查
+        break;
+    }
+    
+    console.log(`更新颜色指示器 ${type}:`, value, '当前RGB:', this.getRgbString());
+    this.checkConsistency();
+    this.updatePreview();
+    // 强制触发变更检测
+    this.cdr.detectChanges();
+    console.log('变更检测已触发');
+  }
+
+  updateSpaceIndicator(key: string, value: number) {
+    if (key in this.spaceIndicators) {
+      (this.spaceIndicators as any)[key] = value;
+      this.checkConsistency();
+    }
+  }
+
+  updateMaterialIndicator(key: string, value: number) {
+    if (key in this.materialIndicators) {
+      (this.materialIndicators as any)[key] = value;
+      this.checkConsistency();
+      this.normalizeMaterialRatios();
+    }
+  }
+
+  // 材质比例归一化
+  private normalizeMaterialRatios() {
+    const total = this.materialIndicators.fabricRatio + 
+                  this.materialIndicators.woodRatio + 
+                  this.materialIndicators.metalRatio;
+    
+    if (total > 100) {
+      const scale = 100 / total;
+      this.materialIndicators.fabricRatio = Math.round(this.materialIndicators.fabricRatio * scale);
+      this.materialIndicators.woodRatio = Math.round(this.materialIndicators.woodRatio * scale);
+      this.materialIndicators.metalRatio = 100 - this.materialIndicators.fabricRatio - this.materialIndicators.woodRatio;
+    }
+  }
+
+  // 预览更新
+  private updatePreview() {
+    // 移除直接DOM操作,依赖Angular的属性绑定
+    console.log('updatePreview调用,当前RGB:', this.getRgbString());
+    // 不再直接操作DOM,让Angular的变更检测处理
+  }
+
+  // 获取RGB字符串
+  getRgbString(): string {
+    const { r, g, b } = this.colorIndicators.mainColor;
+    const rgbString = `rgb(${r}, ${g}, ${b})`;
+    console.log('getRgbString调用:', rgbString, '完整mainColor对象:', this.colorIndicators.mainColor);
+    return rgbString;
+  }
+
+  // 获取色温描述
+  getColorTemperatureDescription(): string {
+    const temp = this.colorIndicators.colorTemperature;
+    if (temp < 3000) return '暖色调';
+    if (temp < 4000) return '中性色调';
+    if (temp < 5000) return '自然色调';
+    return '冷色调';
+  }
+
+  applyPresetAtmosphere(preset: any) {
+    const rgbMatch = preset.rgb.match(/(\d+),(\d+),(\d+)/);
+    if (rgbMatch) {
+      this.colorIndicators.mainColor = {
+        r: parseInt(rgbMatch[1]),
+        g: parseInt(rgbMatch[2]),
+        b: parseInt(rgbMatch[3])
+      };
+    }
+
+    const tempMatch = preset.colorTemp.match(/(\d+)/);
+    if (tempMatch) {
+      this.colorIndicators.colorTemperature = parseInt(tempMatch[1]);
+    }
+
+    if (preset.materials.includes('木质')) {
+      this.materialIndicators.woodRatio = 60;
+      this.materialIndicators.fabricRatio = 30;
+      this.materialIndicators.metalRatio = 10;
+    } else if (preset.materials.includes('金属')) {
+      this.materialIndicators.metalRatio = 50;
+      this.materialIndicators.woodRatio = 20;
+      this.materialIndicators.fabricRatio = 30;
+    }
+
+    this.updatePreview();
+    this.checkConsistency();
+  }
+
+  // 一致性检查
+  private checkConsistency() {
+    this.consistencyWarnings = [];
+    
+    if (this.colorIndicators.colorTemperature < 3000 && 
+        this.colorIndicators.mainColor.r > 200 && 
+        this.colorIndicators.mainColor.g < 150) {
+      this.consistencyWarnings.push('暖调氛围与冷色调RGB值存在矛盾');
+    }
+    
+    if (this.materialIndicators.fabricRatio > 70 && this.colorIndicators.colorTemperature > 5000) {
+      this.consistencyWarnings.push('高布艺占比与冷色调可能不协调');
+    }
+    
+    if (this.spaceIndicators.lineRatio > 80 && this.materialIndicators.woodRatio > 60) {
+      this.consistencyWarnings.push('高直线条占比与高木质占比可能过于刚硬');
+    }
+  }
+
+  // 协作批注功能
+  addCommentToRequirement(requirementId: string, content: string, author: string = '当前用户'): void {
+    const comment: CollaborationComment = {
+      id: this.generateId(),
+      author,
+      role: 'designer',
+      content,
+      timestamp: new Date(),
+      requirementId,
+      status: 'pending'
+    };
+    
+    this.collaborationComments.push(comment);
+    this.updateProgress();
+  }
+
+  // 优先级排序功能
+  updateRequirementPriority(requirementId: string, priority: 'high' | 'medium' | 'low'): void {
+    const requirement = this.requirementItems.find(r => r.id === requirementId);
+    if (requirement) {
+      requirement.priority = priority;
+      requirement.lastUpdated = new Date();
+      this.sortRequirementsByPriority();
+    }
+  }
+
+  sortRequirementsByPriority(): void {
+    const priorityOrder = { 'high': 3, 'medium': 2, 'low': 1 };
+    this.requirementItems.sort((a, b) => {
+      return priorityOrder[b.priority] - priorityOrder[a.priority];
+    });
+  }
+
+  // 历史记录功能
+  saveCurrentState(): void {
+    const currentState = {
+      timestamp: new Date(),
+      colorIndicators: { ...this.colorIndicators },
+      spaceIndicators: { ...this.spaceIndicators },
+      materialIndicators: { ...this.materialIndicators },
+      requirementItems: this.requirementItems.map((r: RequirementItem) => ({ ...r })),
+      author: '当前用户'
+    };
+    
+    this.historyStates.push(currentState);
+    
+    if (this.historyStates.length > 10) {
+      this.historyStates.shift();
+    }
+  }
+
+  restoreHistoryState(index: number): void {
+    if (index >= 0 && index < this.historyStates.length) {
+      const state = this.historyStates[index];
+      this.colorIndicators = { ...state.colorIndicators };
+      this.spaceIndicators = { ...state.spaceIndicators };
+      this.materialIndicators = { ...state.materialIndicators };
+      this.requirementItems = state.requirementItems.map((r: RequirementItem) => ({ ...r }));
+      
+      this.updatePreview();
+      this.checkConsistency();
+    }
+  }
+
+  // 获取未解决的评论数量
+  getUnresolvedCommentsCount(): number {
+    return this.collaborationComments.filter(c => c.status === 'pending').length;
+  }
+
+  // 获取需求的评论
+  getCommentsForRequirement(requirementId: string): CollaborationComment[] {
+    return this.collaborationComments.filter(comment => comment.requirementId === requirementId) || [];
+  }
+
+  // 获取状态样式类
+  getStatusClass(status: string): string {
+    return status;
+  }
+
+  // 工具方法
+  private generateId(): string {
+    return Math.random().toString(36).substr(2, 9);
+  }
+
+  private formatFileSize(bytes: number): string {
+    if (bytes === 0) return '0 Bytes';
+    const k = 1024;
+    const sizes = ['Bytes', 'KB', 'MB', 'GB'];
+    const i = Math.floor(Math.log(bytes) / Math.log(k));
+    return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
+  }
+
+  // 删除素材
+  removeMaterial(materialId: string) {
+    const index = this.materialFiles.findIndex(m => m.id === materialId);
+    if (index > -1) {
+      const material = this.materialFiles[index];
+      if (material.url) {
+        URL.revokeObjectURL(material.url);
+      }
+      this.materialFiles.splice(index, 1);
+    }
+    
+    const materialsIndex = this.materials.findIndex(m => m.id === materialId);
+    if (materialsIndex > -1) {
+      this.materials.splice(materialsIndex, 1);
+    }
+  }
+
+  // 切换标签页
+  switchTab(tab: 'materials' | 'mapping' | 'collaboration' | 'progress') {
+    this.activeTab = tab;
+  }
+
+  // 获取需求状态文本
+  getRequirementStatusText(status: string): string {
+    const statusMap: { [key: string]: string } = {
+      'pending': '待确认',
+      'confirmed': '已确认',
+      'rejected': '已拒绝'
+    };
+    return statusMap[status] || status;
+  }
+
+  // 获取优先级文本
+  getPriorityText(priority: string): string {
+    const priorityMap: { [key: string]: string } = {
+      'high': '高',
+      'medium': '中',
+      'low': '低'
+    };
+    return priorityMap[priority] || priority;
+  }
+
+  // 刷新进度(保留用于手动刷新,但不再强制要求)
+  refreshProgress(): void {
+    // 重新计算进度
+    this.updateProgress();
+    
+    // 更新进度条显示
+    const progress = this.getProgressPercentage();
+    this.updateProgressBar(progress);
+    
+    // 检查阶段完成状态
+    this.checkStageCompletion();
+    
+    // 触发进度更新事件
+    this.progressUpdated.emit(progress);
+    
+    console.log('进度已手动刷新:', progress + '%');
+  }
+
+  // 自动保存功能
+  private triggerAutoSave(): void {
+    if (!this.autoSaveEnabled) return;
+    
+    this.hasUnsavedChanges = true;
+    this.saveStatus = 'unsaved';
+    
+    // 清除之前的定时器
+    if (this.autoSaveTimer) {
+      clearTimeout(this.autoSaveTimer);
+    }
+    
+    // 设置新的自动保存定时器(延迟1秒)
+    this.autoSaveTimer = setTimeout(() => {
+      this.performAutoSave();
+    }, 1000);
+  }
+
+  private async performAutoSave(): Promise<void> {
+    if (this.isSaving) return;
+    
+    this.isSaving = true;
+    this.saveStatus = 'saving';
+    
+    try {
+      // 收集所有需要保存的数据
+      const saveData = this.collectSaveData();
+      
+      // 模拟保存操作(实际项目中这里会调用API)
+      await this.saveToServer(saveData);
+      
+      this.hasUnsavedChanges = false;
+      this.saveStatus = 'saved';
+      this.lastSaveTime = new Date();
+      
+      console.log('自动保存成功:', new Date().toLocaleTimeString());
+    } catch (error) {
+      this.saveStatus = 'error';
+      console.error('自动保存失败:', error);
+    } finally {
+      this.isSaving = false;
+    }
+  }
+
+  // 手动保存
+  async manualSave(): Promise<void> {
+    if (this.isSaving) return;
+    
+    // 清除自动保存定时器
+    if (this.autoSaveTimer) {
+      clearTimeout(this.autoSaveTimer);
+    }
+    
+    await this.performAutoSave();
+  }
+
+  private collectSaveData(): any {
+    return {
+      projectId: this.projectId,
+      colorIndicators: this.colorIndicators,
+      spaceIndicators: this.spaceIndicators,
+      materialIndicators: this.materialIndicators,
+      requirementItems: this.requirementItems,
+      requirementMetrics: this.requirementMetrics,
+      materialFiles: this.materialFiles,
+      collaborationComments: this.collaborationComments,
+      stageCompletionStatus: this.stageCompletionStatus,
+      lastUpdated: new Date()
+    };
+  }
+
+  private async saveToServer(data: any): Promise<void> {
+    // 模拟API调用延迟
+    return new Promise((resolve, reject) => {
+      setTimeout(() => {
+        // 模拟90%成功率
+        if (Math.random() > 0.1) {
+          resolve();
+        } else {
+          reject(new Error('网络错误'));
+        }
+      }, 500);
+    });
+  }
+
+  // 检查阶段完成状态
+  private checkStageCompletion(): void {
+    // 检查素材分析阶段 - 需要有素材文件且都已分析
+    this.stageCompletionStatus.materialAnalysis = this.materialFiles.length > 0 && 
+      this.materialFiles.every(m => m.analysis);
+    
+    // 检查需求映射阶段 - 需要有确认的需求项,或者滑动条数据已调整且保存
+    const hasConfirmedRequirements = this.requirementItems.length > 0 && 
+      this.requirementItems.some(r => r.status === 'confirmed');
+    const hasAdjustedIndicators = this.hasIndicatorChanges();
+    this.stageCompletionStatus.requirementMapping = hasConfirmedRequirements || 
+      (hasAdjustedIndicators && this.saveStatus === 'saved');
+    
+    // 检查协作验证阶段 - 需要有协作评论或需求评论
+    this.stageCompletionStatus.collaboration = this.collaborationComments.length > 0 || 
+      this.requirementItems.some(r => r.comments && r.comments.length > 0);
+    
+    // 检查进度审查阶段 - 需要进度达到80%以上
+    this.stageCompletionStatus.progressReview = this.getProgressPercentage() >= 80;
+    
+    console.log('阶段完成状态更新:', this.stageCompletionStatus);
+    
+    // 检查是否所有阶段都已完成
+    const allStagesCompleted = Object.values(this.stageCompletionStatus).every(status => status);
+    
+    // 发射阶段完成事件
+    if (allStagesCompleted) {
+      this.stageCompleted.emit({ stage: 'requirements-communication', allStagesCompleted: true });
+    }
+  }
+
+  // 检查指示器是否有变化
+  private hasIndicatorChanges(): boolean {
+    // 检查颜色指示器是否有变化(与默认值比较)
+    const defaultColorIndicators = {
+      mainColor: { r: 255, g: 230, b: 180 },
+      colorTemperature: 2700,
+      colorRange: '暖色调',
+      saturation: 70,
+      brightness: 80,
+      contrast: 60
+    };
+    
+    const defaultSpaceIndicators = {
+      lineRatio: 60,
+      blankRatio: 30,
+      flowWidth: 0.9,
+      aspectRatio: 1.6,
+      ceilingHeight: 2.8,
+      lightingLevel: 300
+    };
+    
+    const defaultMaterialIndicators = {
+      fabricRatio: 50,
+      woodRatio: 30,
+      metalRatio: 20,
+      smoothness: 7,
+      glossiness: 4,
+      texture: 6
+    };
+    
+    // 检查颜色指示器变化
+    const colorChanged = JSON.stringify(this.colorIndicators) !== JSON.stringify(defaultColorIndicators);
+    
+    // 检查空间指示器变化
+    const spaceChanged = JSON.stringify(this.spaceIndicators) !== JSON.stringify(defaultSpaceIndicators);
+    
+    // 检查材质指示器变化
+    const materialChanged = JSON.stringify(this.materialIndicators) !== JSON.stringify(defaultMaterialIndicators);
+    
+    return colorChanged || spaceChanged || materialChanged;
+  }
+
+  // 获取阶段状态文本
+  getStageStatusText(stage: keyof typeof this.stageCompletionStatus): string {
+    if (this.stageCompletionStatus[stage]) {
+      return '已完成';
+    } else {
+      // 检查是否为未开始状态
+      const stages = ['materialAnalysis', 'requirementMapping', 'collaboration', 'progressReview'];
+      const currentIndex = stages.indexOf(stage);
+      
+      // 检查前面的阶段是否都已完成
+      let allPreviousCompleted = true;
+      for (let i = 0; i < currentIndex; i++) {
+        if (!this.stageCompletionStatus[stages[i] as keyof typeof this.stageCompletionStatus]) {
+          allPreviousCompleted = false;
+          break;
+        }
+      }
+      
+      return allPreviousCompleted ? '进行中' : '未进行';
+    }
+  }
+
+  // 获取阶段状态类名
+  getStageStatusClass(stage: keyof typeof this.stageCompletionStatus): string {
+    if (this.stageCompletionStatus[stage]) {
+      return 'stage-completed';
+    } else {
+      // 检查是否为未开始状态
+      const stages = ['materialAnalysis', 'requirementMapping', 'collaboration', 'progressReview'];
+      const currentIndex = stages.indexOf(stage);
+      
+      // 检查前面的阶段是否都已完成
+      let allPreviousCompleted = true;
+      for (let i = 0; i < currentIndex; i++) {
+        if (!this.stageCompletionStatus[stages[i] as keyof typeof this.stageCompletionStatus]) {
+          allPreviousCompleted = false;
+          break;
+        }
+      }
+      
+      return allPreviousCompleted ? 'stage-in-progress' : 'stage-pending';
+    }
+  }
+
+  // 检查是否可以进入下一阶段
+  canProceedToNextStage(currentStage: keyof typeof this.stageCompletionStatus): boolean {
+    const stages = Object.keys(this.stageCompletionStatus) as (keyof typeof this.stageCompletionStatus)[];
+    const currentIndex = stages.indexOf(currentStage);
+    
+    // 检查当前阶段之前的所有阶段是否都已完成
+    for (let i = 0; i <= currentIndex; i++) {
+      if (!this.stageCompletionStatus[stages[i]]) {
+        return false;
+      }
+    }
+    
+    return true;
+  }
+
+  // 获取保存状态文本
+  getSaveStatusText(): string {
+    switch (this.saveStatus) {
+      case 'saved': return this.lastSaveTime ? `已保存 ${this.lastSaveTime.toLocaleTimeString()}` : '已保存';
+      case 'saving': return '保存中...';
+      case 'error': return '保存失败';
+      case 'unsaved': return '有未保存的更改';
+      default: return '';
+    }
+  }
+
+  // 获取保存状态图标
+  getSaveStatusIcon(): string {
+    switch (this.saveStatus) {
+      case 'saved': return '✓';
+      case 'saving': return '⟳';
+      case 'error': return '⚠';
+      case 'unsaved': return '●';
+      default: return '';
+    }
+  }
+}

+ 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[] = [];
+}

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

@@ -0,0 +1,111 @@
+<div class="settlement-card">
+  <!-- 统计数据概览 -->
+  <div class="stats-overview">
+    <h4>尾款结算概览</h4>
+    <div class="stats-grid">
+      <div class="stat-item total">
+        <div class="stat-value">{{ formatAmount(stats().totalAmount) }}</div>
+        <div class="stat-label">总金额 ({{ stats().totalCount }}项)</div>
+      </div>
+      <div class="stat-item pending">
+        <div class="stat-value">{{ formatAmount(stats().pendingAmount) }}</div>
+        <div class="stat-label">待收款 ({{ stats().pendingCount }}项)</div>
+      </div>
+      <div class="stat-item overdue">
+        <div class="stat-value">{{ formatAmount(stats().overdueAmount) }}</div>
+        <div class="stat-label">逾期尾款 ({{ stats().overdueCount }}项)</div>
+      </div>
+      <div class="stat-item completed">
+        <div class="stat-value">{{ formatAmount(stats().completedAmount) }}</div>
+        <div class="stat-label">已收款 ({{ stats().completedCount }}项)</div>
+      </div>
+    </div>
+  </div>
+
+  <!-- 筛选和搜索 -->
+  <div class="filter-section">
+    <div class="search-bar">
+      <input 
+        type="text" 
+        placeholder="搜索项目名称或阶段..."
+        [value]="searchKeyword()"
+        (input)="updateSearchKeyword($event.target.value)"
+        class="search-input">
+    </div>
+    <div class="filter-buttons">
+      <button 
+        class="filter-btn"
+        [class.active]="statusFilter() === 'all'"
+        (click)="updateStatusFilter('all')">
+        全部
+      </button>
+      <button 
+        class="filter-btn pending"
+        [class.active]="statusFilter() === '待结算'"
+        (click)="updateStatusFilter('待结算')">
+        待结算
+      </button>
+      <button 
+        class="filter-btn overdue"
+        [class.active]="statusFilter() === 'overdue'"
+        (click)="updateStatusFilter('overdue')">
+        逾期
+      </button>
+      <button 
+        class="filter-btn completed"
+        [class.active]="statusFilter() === '已结算'"
+        (click)="updateStatusFilter('已结算')">
+        已结算
+      </button>
+    </div>
+  </div>
+
+  <!-- 结算列表 -->
+  <div class="settlements-list">
+    @if (filteredSettlements() && filteredSettlements().length > 0) {
+      <div class="list-header">
+        <span class="col-project">项目信息</span>
+        <span class="col-amount">金额</span>
+        <span class="col-status">状态</span>
+        <span class="col-date">日期</span>
+      </div>
+      <div class="list-body">
+        @for (settlement of filteredSettlements(); track settlement.id) {
+          <div class="settlement-item" [class]="getStatusClass(settlement)">
+            <div class="col-project">
+              <div class="project-name">{{ settlement.projectName || '结算项' }}</div>
+              <div class="stage-name">{{ settlement.stage || '阶段' }}</div>
+            </div>
+            <div class="col-amount">
+              <div class="amount">{{ formatAmount(settlement.amount || 0) }}</div>
+              <div class="percentage">{{ settlement.percentage }}%</div>
+            </div>
+            <div class="col-status">
+              <span class="status-badge" [class]="getStatusClass(settlement)">
+                {{ settlement.status }}
+              </span>
+              @if (isOverdue(settlement)) {
+                <div class="overdue-days">逾期 {{ getDaysOverdue(settlement) }} 天</div>
+              }
+            </div>
+            <div class="col-date">
+              @if (settlement.settledAt) {
+                <div class="settled-date">{{ settlement.settledAt | date:'yyyy-MM-dd' }}</div>
+                <div class="date-label">结算日期</div>
+              } @else {
+                <div class="due-date">{{ settlement.createdAt | date:'yyyy-MM-dd' }}</div>
+                <div class="date-label">创建日期</div>
+              }
+            </div>
+          </div>
+        }
+      </div>
+    } @else {
+      <div class="empty-state">
+        <div class="empty-icon">💰</div>
+        <div class="empty-title">暂无结算信息</div>
+        <div class="empty-desc">当前筛选条件下没有找到相关的结算记录</div>
+      </div>
+    }
+  </div>
+</div>

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

@@ -0,0 +1,330 @@
+@use '../../styles/_ios-theme.scss' as *;
+
+:host { 
+  display: block; 
+  height: 100%; 
+}
+
+.settlement-card {
+  background: white;
+  border-radius: 12px;
+  padding: 20px;
+  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
+  border: 1px solid $ios-border;
+
+  // 统计数据概览
+  .stats-overview {
+    margin-bottom: 24px;
+
+    h4 {
+      margin: 0 0 16px 0;
+      font-size: 18px;
+      font-weight: $ios-font-weight-semibold;
+      color: $ios-text-primary;
+    }
+
+    .stats-grid {
+      display: grid;
+      grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
+      gap: 16px;
+
+      .stat-item {
+        background: #f8f9fa;
+        border-radius: 8px;
+        padding: 16px;
+        text-align: center;
+        border: 1px solid $ios-border;
+
+        &.total {
+          background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+          color: white;
+          border: none;
+        }
+
+        &.pending {
+          background: linear-gradient(135deg, #ffecd2 0%, #fcb69f 100%);
+          color: #8b4513;
+        }
+
+        &.overdue {
+          background: linear-gradient(135deg, #ff9a9e 0%, #fecfef 100%);
+          color: #dc3545;
+        }
+
+        &.completed {
+          background: linear-gradient(135deg, #a8edea 0%, #fed6e3 100%);
+          color: #28a745;
+        }
+
+        .stat-value {
+          font-size: 24px;
+          font-weight: $ios-font-weight-bold;
+          margin-bottom: 4px;
+        }
+
+        .stat-label {
+          font-size: 12px;
+          opacity: 0.8;
+        }
+      }
+    }
+  }
+
+  // 筛选和搜索区域
+  .filter-section {
+    margin-bottom: 20px;
+    display: flex;
+    flex-direction: column;
+    gap: 12px;
+
+    .search-bar {
+      .search-input {
+        width: 100%;
+        padding: 10px 16px;
+        border: 1px solid $ios-border;
+        border-radius: 8px;
+        font-size: 14px;
+        background: white;
+        transition: border-color 0.2s ease;
+
+        &:focus {
+          outline: none;
+          border-color: $ios-primary;
+          box-shadow: 0 0 0 2px rgba(0, 122, 255, 0.1);
+        }
+
+        &::placeholder {
+          color: $ios-text-secondary;
+        }
+      }
+    }
+
+    .filter-buttons {
+      display: flex;
+      gap: 8px;
+      flex-wrap: wrap;
+
+      .filter-btn {
+        padding: 8px 16px;
+        border: 1px solid $ios-border;
+        border-radius: 20px;
+        background: white;
+        color: $ios-text-secondary;
+        font-size: 14px;
+        cursor: pointer;
+        transition: all 0.2s ease;
+
+        &:hover {
+          background: #f8f9fa;
+        }
+
+        &.active {
+          background: $ios-primary;
+          color: white;
+          border-color: $ios-primary;
+        }
+
+        &.pending.active {
+          background: #ff9500;
+          border-color: #ff9500;
+        }
+
+        &.overdue.active {
+          background: #dc3545;
+          border-color: #dc3545;
+        }
+
+        &.completed.active {
+          background: #28a745;
+          border-color: #28a745;
+        }
+      }
+    }
+  }
+
+  // 结算列表
+  .settlements-list {
+    .list-header {
+      display: grid;
+      grid-template-columns: 2fr 1fr 1fr 1fr;
+      gap: 16px;
+      padding: 12px 16px;
+      background: #f8f9fa;
+      border-radius: 8px;
+      font-weight: $ios-font-weight-semibold;
+      color: $ios-text-secondary;
+      font-size: 12px;
+      text-transform: uppercase;
+      letter-spacing: 0.5px;
+      margin-bottom: 8px;
+    }
+
+    .list-body {
+      .settlement-item {
+        display: grid;
+        grid-template-columns: 2fr 1fr 1fr 1fr;
+        gap: 16px;
+        padding: 16px;
+        border: 1px solid $ios-border;
+        border-radius: 8px;
+        margin-bottom: 8px;
+        background: white;
+        transition: all 0.2s ease;
+
+        &:hover {
+          box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+          transform: translateY(-1px);
+        }
+
+        &.overdue {
+          border-left: 4px solid #dc3545;
+          background: #fff5f5;
+        }
+
+        &.pending {
+          border-left: 4px solid #ff9500;
+        }
+
+        &.completed {
+          border-left: 4px solid #28a745;
+          background: #f8fff9;
+        }
+
+        .col-project {
+          .project-name {
+            font-weight: $ios-font-weight-semibold;
+            color: $ios-text-primary;
+            margin-bottom: 4px;
+          }
+
+          .stage-name {
+            font-size: 12px;
+            color: $ios-text-secondary;
+          }
+        }
+
+        .col-amount {
+          .amount {
+            font-weight: $ios-font-weight-bold;
+            color: $ios-primary;
+            font-size: 16px;
+          }
+
+          .percentage {
+            font-size: 12px;
+            color: $ios-text-secondary;
+            margin-top: 2px;
+          }
+        }
+
+        .col-status {
+          .status-badge {
+            display: inline-block;
+            padding: 4px 8px;
+            border-radius: 12px;
+            font-size: 12px;
+            font-weight: $ios-font-weight-medium;
+
+            &.pending {
+              background: rgba(255, 149, 0, 0.1);
+              color: #ff9500;
+            }
+
+            &.overdue {
+              background: rgba(220, 53, 69, 0.1);
+              color: #dc3545;
+            }
+
+            &.completed {
+              background: rgba(40, 167, 69, 0.1);
+              color: #28a745;
+            }
+          }
+
+          .overdue-days {
+            font-size: 11px;
+            color: #dc3545;
+            margin-top: 4px;
+            font-weight: $ios-font-weight-medium;
+          }
+        }
+
+        .col-date {
+          text-align: right;
+
+          .settled-date,
+          .due-date {
+            font-weight: $ios-font-weight-medium;
+            color: $ios-text-primary;
+          }
+
+          .date-label {
+            font-size: 11px;
+            color: $ios-text-secondary;
+            margin-top: 2px;
+          }
+        }
+      }
+    }
+  }
+
+  // 空状态
+  .empty-state {
+    text-align: center;
+    padding: 40px 20px;
+    color: $ios-text-secondary;
+
+    .empty-icon {
+      font-size: 48px;
+      margin-bottom: 16px;
+      opacity: 0.5;
+    }
+
+    .empty-title {
+      font-size: 16px;
+      font-weight: $ios-font-weight-semibold;
+      color: $ios-text-primary;
+      margin-bottom: 8px;
+    }
+
+    .empty-desc {
+      font-size: 14px;
+      line-height: 1.4;
+    }
+  }
+}
+
+// 响应式设计
+@media (max-width: 768px) {
+  .settlement-card {
+    .stats-overview .stats-grid {
+      grid-template-columns: repeat(2, 1fr);
+    }
+
+    .filter-section {
+      .filter-buttons {
+        justify-content: center;
+      }
+    }
+
+    .settlements-list {
+      .list-header {
+        display: none;
+      }
+
+      .list-body .settlement-item {
+        grid-template-columns: 1fr;
+        gap: 8px;
+
+        .col-project,
+        .col-amount,
+        .col-status,
+        .col-date {
+          display: flex;
+          justify-content: space-between;
+          align-items: center;
+        }
+      }
+    }
+  }
+}

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

@@ -0,0 +1,118 @@
+import { Component, Input, computed, signal } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FormsModule } from '@angular/forms';
+import { Settlement } from '../../../models/project.model';
+
+export interface SettlementStats {
+  totalAmount: number;
+  pendingAmount: number;
+  overdueAmount: number;
+  completedAmount: number;
+  totalCount: number;
+  pendingCount: number;
+  overdueCount: number;
+  completedCount: number;
+}
+
+@Component({
+  selector: 'app-settlement-card',
+  standalone: true,
+  imports: [CommonModule, FormsModule],
+  templateUrl: './settlement-card.html',
+  styleUrls: ['./settlement-card.scss']
+})
+export class SettlementCardComponent {
+  @Input() settlements: Settlement[] = [];
+  
+  // 筛选条件
+  statusFilter = signal<string>('all');
+  searchKeyword = signal<string>('');
+  
+  // 计算统计数据
+  stats = computed<SettlementStats>(() => {
+    const settlements = this.settlements || [];
+    
+    return {
+      totalAmount: settlements.reduce((sum, s) => sum + (s.amount || 0), 0),
+      pendingAmount: settlements.filter(s => s.status === '待结算').reduce((sum, s) => sum + (s.amount || 0), 0),
+      overdueAmount: settlements.filter(s => this.isOverdue(s)).reduce((sum, s) => sum + (s.amount || 0), 0),
+      completedAmount: settlements.filter(s => s.status === '已结算').reduce((sum, s) => sum + (s.amount || 0), 0),
+      totalCount: settlements.length,
+      pendingCount: settlements.filter(s => s.status === '待结算').length,
+      overdueCount: settlements.filter(s => this.isOverdue(s)).length,
+      completedCount: settlements.filter(s => s.status === '已结算').length
+    };
+  });
+  
+  // 筛选后的结算列表
+  filteredSettlements = computed(() => {
+    let filtered = this.settlements || [];
+    
+    // 状态筛选
+    const status = this.statusFilter();
+    if (status !== 'all') {
+      if (status === 'overdue') {
+        filtered = filtered.filter(s => this.isOverdue(s));
+      } else {
+        filtered = filtered.filter(s => s.status === status);
+      }
+    }
+    
+    // 关键词搜索
+    const keyword = this.searchKeyword().toLowerCase();
+    if (keyword) {
+      filtered = filtered.filter(s => 
+        (s.projectName || '').toLowerCase().includes(keyword) ||
+        (s.stage || '').toLowerCase().includes(keyword)
+      );
+    }
+    
+    return filtered;
+  });
+  
+  // 判断是否逾期
+  isOverdue(settlement: Settlement): boolean {
+    if (settlement.status === '已结算') return false;
+    // 简化逾期判断:如果是待结算状态且创建时间超过30天,则认为逾期
+    const thirtyDaysAgo = new Date();
+    thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);
+    return settlement.createdAt < thirtyDaysAgo;
+  }
+  
+  // 获取状态样式类
+  getStatusClass(settlement: Settlement): string {
+    if (this.isOverdue(settlement)) return 'overdue';
+    return settlement.status === '已结算' ? 'completed' : 'pending';
+  }
+  
+  // 格式化金额
+  formatAmount(amount: number): string {
+    return new Intl.NumberFormat('zh-CN', {
+      style: 'currency',
+      currency: 'CNY',
+      minimumFractionDigits: 0,
+      maximumFractionDigits: 2
+    }).format(amount);
+  }
+  
+  // 更新筛选条件
+  updateStatusFilter(status: string): void {
+    this.statusFilter.set(status);
+  }
+  
+  updateSearchKeyword(keyword: string): void {
+    this.searchKeyword.set(keyword);
+  }
+  
+  // 计算逾期天数
+  getDaysOverdue(settlement: Settlement): number {
+    if (settlement.status === '已结算') return 0;
+    const thirtyDaysAgo = new Date();
+    thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);
+    if (settlement.createdAt >= thirtyDaysAgo) return 0;
+    
+    const today = new Date();
+    const diffTime = today.getTime() - thirtyDaysAgo.getTime();
+    return Math.max(0, Math.ceil(diffTime / (1000 * 60 * 60 * 24)));
+  }
+}

+ 300 - 230
src/app/shared/styles/_hr-dialog.scss

@@ -1,33 +1,32 @@
 @use 'variables';
-@use 'ios-theme';
- 
+@use 'ios-theme' as ios;
 // HR模块通用对话框样式
 // 基于新增员工面板的设计,提供统一的对话框外观
 
 // 对话框容器样式
 .hr-dialog {
   .mat-mdc-dialog-container .mdc-dialog__surface {
-    border-radius: $ios-radius-lg;
-    background: $ios-card-background;
-    box-shadow: $ios-shadow-lg;
-    border: 1px solid $ios-border;
+    border-radius: ios.$ios-radius-lg;
+    background: ios.$ios-card-background;
+    box-shadow: ios.$ios-shadow-lg;
+    border: 1px solid ios.$ios-border;
     min-width: 500px;
     max-width: 700px;
   }
 
   .mat-mdc-dialog-title {
-    color: $ios-text-primary;
-    font-weight: $ios-font-weight-semibold;
-    font-family: $ios-font-family;
+    color: ios.$ios-text-primary;
+    font-weight: ios.$ios-font-weight-semibold;
+    font-family: ios.$ios-font-family;
     display: flex;
     align-items: center;
-    gap: $ios-spacing-sm;
-    padding: $ios-spacing-lg $ios-spacing-lg $ios-spacing-md;
+    gap: ios.$ios-spacing-sm;
+    padding: ios.$ios-spacing-lg ios.$ios-spacing-lg ios.$ios-spacing-md;
     margin: 0;
-    border-bottom: 1px solid $ios-border;
+    border-bottom: 1px solid ios.$ios-border;
 
     mat-icon {
-      color: $ios-primary;
+      color: ios.$ios-primary;
       font-size: 24px;
       width: 24px;
       height: 24px;
@@ -35,8 +34,8 @@
   }
 
   .mat-mdc-dialog-content {
-    padding: $ios-spacing-lg;
-    font-family: $ios-font-family;
+    padding: ios.$ios-spacing-lg;
+    font-family: ios.$ios-font-family;
     max-height: 70vh;
     overflow-y: auto;
 
@@ -45,35 +44,98 @@
     .dialog-content {
       display: flex;
       flex-direction: column;
-      gap: $ios-spacing-md;
+      gap: ios.$ios-spacing-md;
     }
 
-    .form-row,
-    .form-section {
+    .form-row {
       display: flex;
-      gap: $ios-spacing-md;
-      margin-bottom: $ios-spacing-sm;
-      
-      &.single-column {
-        flex-direction: column;
-      }
-      
-      mat-form-field {
+      gap: ios.$ios-spacing-md;
+      margin-bottom: ios.$ios-spacing-sm;
+
+      .form-group {
         flex: 1;
+      }
+    }
+
+    .form-group {
+      display: flex;
+      flex-direction: column;
+      margin-bottom: ios.$ios-spacing-xs;
+
+      label {
+        font-weight: 500;
+        margin-bottom: 6px;
+        color: #333;
+        font-size: 14px;
+        margin-bottom: ios.$ios-spacing-xs;
+      }
+
+      .mat-mdc-form-field {
+        border-radius: ios.$ios-radius-md;
         
-        &.full-width {
-          width: 100%;
-          margin-bottom: $ios-spacing-xs;
+        .mat-mdc-text-field-wrapper {
+          background: white;
+          border: 1px solid #e0e0e0;
+          border-radius: 8px;
+          transition: all 0.3s ease;
+
+          &:hover {
+            border-color: ios.$ios-primary;
+          }
+        }
+
+        .mat-mdc-form-field-subscript-wrapper {
+          color: ios.$ios-text-secondary;
+          font-family: ios.$ios-font-family;
+          font-weight: ios.$ios-font-weight-medium;
+        }
+
+        .mat-mdc-form-field-infix {
+          color: ios.$ios-text-primary;
+          font-family: ios.$ios-font-family;
+          padding: ios.$ios-spacing-sm;
+        }
+
+        .mat-mdc-form-field-error {
+          color: ios.$ios-error;
+          font-family: ios.$ios-font-family;
+          font-size: ios.$ios-font-size-caption-1;
+          margin-top: ios.$ios-spacing-xs;
+        }
+
+        // 选择框样式
+        .mat-mdc-select-value {
+          color: ios.$ios-text-primary;
+          font-family: ios.$ios-font-family;
+          padding: ios.$ios-spacing-sm 0;
+        }
+
+        .mat-mdc-select-arrow {
+          color: ios.$ios-text-secondary;
+          transition: transform 0.3s ease;
+        }
+
+        &.mat-focused .mat-mdc-select-arrow {
+          color: ios.$ios-primary;
+        }
+
+        // 输入框样式
+        .mat-mdc-input-element {
+          color: ios.$ios-text-secondary;
+        }
+
+        &.mat-focused .mat-mdc-input-element {
+          color: ios.$ios-primary;
         }
       }
     }
 
     // 表单字段样式
     mat-form-field {
-      margin-bottom: $ios-spacing-xs;
+      margin-bottom: ios.$ios-spacing-xs;
       
       .mat-mdc-form-field-outline {
-        border-radius: $ios-radius-md;
+        border-radius: ios.$ios-radius-md;
         border-color: rgba(0, 122, 255, 0.2);
         transition: all 0.3s ease;
       }
@@ -83,113 +145,111 @@
       }
 
       &.mat-focused .mat-mdc-form-field-outline {
-        border-color: $ios-primary;
+        border-color: ios.$ios-primary;
         border-width: 2px;
         box-shadow: 0 0 0 3px rgba(0, 122, 255, 0.1);
       }
 
       .mat-mdc-form-field-label {
-        color: $ios-text-secondary;
-        font-family: $ios-font-family;
-        font-weight: $ios-font-weight-medium;
+        color: ios.$ios-text-secondary;
+        font-family: ios.$ios-font-family;
+        font-weight: ios.$ios-font-weight-medium;
       }
 
       .mat-mdc-input-element {
-        color: $ios-text-primary;
-        font-family: $ios-font-family;
-        padding: $ios-spacing-sm;
+        color: ios.$ios-text-primary;
+        font-family: ios.$ios-font-family;
+        padding: ios.$ios-spacing-sm;
       }
 
-      .mat-mdc-form-field-error {
-        color: $ios-error;
-        font-family: $ios-font-family;
-        font-size: $ios-font-size-caption;
-        margin-top: $ios-spacing-xs;
+      .mat-mdc-form-field-error.error-message {
+        color: ios.$ios-error;
+        font-family: ios.$ios-font-family;
+        font-size: ios.$ios-font-size-caption-1;
+        margin-top: ios.$ios-spacing-xs;
       }
 
       // 选择框样式
       .mat-mdc-select-value {
-        color: $ios-text-primary;
-        font-family: $ios-font-family;
-        padding: $ios-spacing-sm 0;
+        color: ios.$ios-text-primary;
+        font-family: ios.$ios-font-family;
+        padding: ios.$ios-spacing-sm 0;
       }
 
       .mat-mdc-select-arrow {
-        color: $ios-text-secondary;
+        color: ios.$ios-text-secondary;
         transition: transform 0.3s ease;
       }
 
       &.mat-focused .mat-mdc-select-arrow {
         transform: rotate(180deg);
-        color: $ios-primary;
+        color: ios.$ios-primary;
       }
 
       // 后缀图标样式
       .mat-mdc-form-field-icon-suffix {
-        color: $ios-text-secondary;
+        color: ios.$ios-text-secondary;
         transition: color 0.3s ease;
       }
 
       &:hover .mat-mdc-form-field-icon-suffix {
-        color: $ios-primary;
+        color: ios.$ios-primary;
       }
     }
 
-    // 网格布局样式
-    .metrics-grid {
-      display: grid;
-      grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
-      gap: $ios-spacing-md;
+    // 信息展示区域
+    .info-section {
+      display: flex;
+      flex-direction: column;
+      gap: ios.$ios-spacing-md;
     }
 
-    // 预览区域
-    .preview-section {
-      background: $ios-background-secondary;
-      border: 1px solid $ios-border;
-      border-radius: $ios-radius-lg;
-      padding: $ios-spacing-lg;
-      margin-top: $ios-spacing-lg;
-      transition: all 0.3s ease;
+    .info-card {
+      background: ios.$ios-background-secondary;
+      border: 1px solid ios.$ios-border;
+      border-radius: ios.$ios-radius-lg;
+      padding: ios.$ios-spacing-lg;
+      margin-top: ios.$ios-spacing-lg;
 
       &:hover {
-        box-shadow: $ios-shadow-md;
-        border-color: rgba(0, 122, 255, 0.3);
+        transform: translateY(-2px);
+        box-shadow: ios.$ios-shadow-md;
       }
 
-      .preview-title {
-        font-family: $ios-font-family;
-        font-weight: $ios-font-weight-semibold;
-        color: $ios-text-primary;
-        margin-bottom: $ios-spacing-md;
-        font-size: $ios-font-size-body;
+      .card-title {
+        font-family: ios.$ios-font-family;
+        font-weight: ios.$ios-font-weight-semibold;
+        color: ios.$ios-text-primary;
+        margin-bottom: ios.$ios-spacing-md;
+        font-size: ios.$ios-font-size-body;
         display: flex;
         align-items: center;
-        gap: $ios-spacing-xs;
+        gap: ios.$ios-spacing-xs;
 
-        &::before {
-          content: '👁️';
-          font-size: 16px;
+        mat-icon {
+          color: #007AFF;
+          font-size: 20px;
+          width: 20px;
+          height: 20px;
         }
       }
 
-      .item-preview-card {
-        background: white;
-        border: 1px solid $ios-border;
-        border-radius: $ios-radius-md;
-        padding: $ios-spacing-lg;
-        box-shadow: $ios-shadow-sm;
-        transition: all 0.3s ease;
+      .card-content {
+        border: 1px solid ios.$ios-border;
+        border-radius: ios.$ios-radius-md;
+        padding: ios.$ios-spacing-lg;
+        box-shadow: ios.$ios-shadow-sm;
 
         &:hover {
-          transform: translateY(-2px);
-          box-shadow: $ios-shadow-md;
+          transform: translateY(-1px);
+          box-shadow: ios.$ios-shadow-md;
         }
 
         .preview-header {
           display: flex;
           align-items: center;
-          gap: $ios-spacing-md;
-          margin-bottom: $ios-spacing-md;
+          gap: ios.$ios-spacing-md;
+          margin-bottom: ios.$ios-spacing-md;
 
           .preview-icon {
             width: 32px;
@@ -197,7 +257,7 @@
             display: flex;
             align-items: center;
             justify-content: center;
-            border-radius: $ios-radius-md;
+            border-radius: ios.$ios-radius-md;
             background: rgba(0, 122, 255, 0.1);
             transition: all 0.3s ease;
 
@@ -211,206 +271,216 @@
             flex: 1;
 
             .preview-category {
-              font-size: $ios-font-size-caption;
-              color: $ios-text-secondary;
-              font-family: $ios-font-family;
+              font-size: ios.$ios-font-size-caption-1;
+              color: ios.$ios-text-secondary;
+              font-family: ios.$ios-font-family;
               text-transform: uppercase;
               letter-spacing: 0.5px;
-              margin-bottom: $ios-spacing-xs;
+              margin-bottom: ios.$ios-spacing-xs;
             }
 
             .preview-name {
-              font-size: $ios-font-size-body;
-              color: $ios-text-primary;
-              font-family: $ios-font-family;
-              font-weight: $ios-font-weight-medium;
+              font-size: ios.$ios-font-size-body;
+              color: ios.$ios-text-primary;
+              font-family: ios.$ios-font-family;
+              font-weight: ios.$ios-font-weight-medium;
             }
           }
         }
 
-        .metrics-preview {
+        .content-grid {
+          display: grid;
+          grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
+          gap: ios.$ios-spacing-md;
+          margin-bottom: ios.$ios-spacing-md;
+        }
+
+        .content-item {
           display: flex;
           flex-direction: column;
-          gap: $ios-spacing-sm;
-          padding: $ios-spacing-md;
-          background: $ios-background-secondary;
-          border-radius: $ios-radius-sm;
-
-          .metric-name {
-            font-size: $ios-font-size-caption;
-            color: $ios-text-secondary;
-            font-family: $ios-font-family;
+          padding: 12px;
+          background: #f8f9fa;
+          border-radius: ios.$ios-radius-md;
+
+          .label {
+            font-size: ios.$ios-font-size-caption-1;
+            color: ios.$ios-text-secondary;
+            font-family: ios.$ios-font-family;
             text-transform: uppercase;
             letter-spacing: 0.5px;
+            margin-bottom: ios.$ios-spacing-xs;
+          }
+
+          .value {
+            font-size: ios.$ios-font-size-body;
+            color: ios.$ios-text-primary;
+            font-family: ios.$ios-font-family;
+            font-weight: ios.$ios-font-weight-medium;
+          }
+        }
+
+        .highlight-section {
+          display: flex;
+          align-items: center;
+          gap: ios.$ios-spacing-sm;
+          padding: ios.$ios-spacing-md;
+          background: ios.$ios-background-secondary;
+          border-radius: ios.$ios-radius-sm;
+
+          .highlight-label {
+            font-size: ios.$ios-font-size-caption-1;
+            color: ios.$ios-text-secondary;
+            font-family: ios.$ios-font-family;
           }
 
-          .metric-value {
-            font-size: $ios-font-size-title2;
-            font-weight: $ios-font-weight-bold;
-            color: $ios-primary;
-            font-family: $ios-font-family;
+          .highlight-value {
+            font-size: ios.$ios-font-size-title-2;
+            font-weight: ios.$ios-font-weight-bold;
+            color: ios.$ios-primary;
+            font-family: ios.$ios-font-family;
             display: flex;
-            align-items: baseline;
-            gap: $ios-spacing-xs;
+            align-items: center;
+            gap: ios.$ios-spacing-xs;
 
-            .metric-unit {
-              font-size: $ios-font-size-caption;
-              color: $ios-text-secondary;
-              font-weight: $ios-font-weight-regular;
+            .currency {
+              font-size: ios.$ios-font-size-caption-1;
+              color: ios.$ios-text-secondary;
+              font-weight: ios.$ios-font-weight-regular;
             }
           }
         }
       }
     }
 
-    // 分组标题样式
-    h3 {
-      margin: $ios-spacing-lg 0 $ios-spacing-md 0;
-      color: $ios-text-primary;
-      font-size: $ios-font-size-lg;
-      font-weight: $ios-font-weight-semibold;
-      font-family: $ios-font-family;
-
-      &:first-child {
-        margin-top: 0;
-      }
+    .section-title {
+      margin: ios.$ios-spacing-lg 0 ios.$ios-spacing-md 0;
+      color: ios.$ios-text-primary;
+      font-size: ios.$ios-font-size-lg;
+      font-weight: ios.$ios-font-weight-semibold;
+      font-family: ios.$ios-font-family;
+      border-bottom: 2px solid #007AFF;
+      padding-bottom: 8px;
+      display: flex;
+      align-items: center;
+      gap: 8px;
     }
   }
 
   .mat-mdc-dialog-actions {
-    padding: $ios-spacing-md $ios-spacing-lg $ios-spacing-lg;
-    border-top: 1px solid $ios-border;
-    gap: $ios-spacing-sm;
+    padding: ios.$ios-spacing-md ios.$ios-spacing-lg ios.$ios-spacing-lg;
+    border-top: 1px solid ios.$ios-border;
+    gap: ios.$ios-spacing-sm;
 
     .mat-mdc-button {
-      font-family: $ios-font-family;
-      border-radius: $ios-radius-md;
-      padding: $ios-spacing-sm $ios-spacing-lg;
-      
-      &[color="primary"] {
-        background: $ios-primary;
+      font-family: ios.$ios-font-family;
+      border-radius: ios.$ios-radius-md;
+      padding: ios.$ios-spacing-sm ios.$ios-spacing-lg;
+      font-weight: 500;
+
+      &.mat-primary {
+        background: ios.$ios-primary;
         color: white;
-        
+        border: none;
+
         &:hover {
-          background: rgba(0, 122, 255, 0.9);
+          background: #0056CC;
         }
-        
+
         &:disabled {
-          background: $ios-text-tertiary;
+          background: ios.$ios-text-tertiary;
           color: white;
         }
       }
-      
-      &:not([color]) {
-        color: $ios-text-secondary;
-        
+
+      &.mat-stroked-button {
+        color: ios.$ios-text-secondary;
+        border: 1px solid #e0e0e0;
+
         &:hover {
-          background: rgba(0, 122, 255, 0.05);
-          color: $ios-primary;
+          color: ios.$ios-primary;
+          border-color: #007AFF;
         }
       }
-    }
 
-    .mat-mdc-raised-button {
-      box-shadow: $ios-shadow-sm;
-      
       &:hover {
-        box-shadow: $ios-shadow-md;
+        box-shadow: ios.$ios-shadow-sm;
+      }
+
+      &:active {
+        box-shadow: ios.$ios-shadow-md;
+        transform: translateY(1px);
       }
     }
   }
 }
 
-// 图标颜色类
-.tech-icon { color: #2196F3; }
-.design-icon { color: #E91E63; }
-.product-icon { color: #FF9800; }
-.operation-icon { color: #4CAF50; }
-.hr-icon { color: #9C27B0; }
-.data-icon { color: #607D8B; }
-.marketing-icon { color: #FF5722; }
-.support-icon { color: #795548; }
-.new-icon { color: #00BCD4; }
-
-// 对话框背景遮罩样式
-.hr-dialog-backdrop {
-  background: rgba(0, 0, 0, 0.4);
-  backdrop-filter: blur(4px);
-}
-
-// 选择框下拉面板样式
-.mat-mdc-select-panel {
-  background: white;
-  border-radius: $ios-radius-md;
-  box-shadow: $ios-shadow-lg;
-  border: 1px solid $ios-border;
-  max-height: 300px;
-  overflow-y: auto;
-
-  .mat-mdc-option {
-    font-family: $ios-font-family;
-    padding: $ios-spacing-sm $ios-spacing-md;
-    transition: all 0.2s ease;
-    border-bottom: 1px solid rgba(0, 0, 0, 0.05);
-
-    &:last-child {
-      border-bottom: none;
-    }
-
-    &:hover {
-      background: rgba(0, 122, 255, 0.05);
-      color: $ios-primary;
-    }
+// 特殊样式:员工详情对话框
+.employee-detail-dialog {
+  .mat-mdc-dialog-container .mdc-dialog__surface {
+    min-width: 600px;
+    max-width: 800px;
+  }
 
-    &.mat-mdc-option-active {
-      background: rgba(0, 122, 255, 0.1);
-      color: $ios-primary;
-      font-weight: $ios-font-weight-medium;
-    }
+  .employee-avatar {
+    width: 80px;
+    height: 80px;
+    border-radius: ios.$ios-radius-md;
+    box-shadow: ios.$ios-shadow-lg;
+    border: 1px solid ios.$ios-border;
+    object-fit: cover;
+  }
 
-    .mat-mdc-option-text {
-      display: flex;
-      align-items: center;
-      gap: $ios-spacing-sm;
+  .employee-info {
+    display: flex;
+    align-items: center;
+    gap: 20px;
+    font-family: ios.$ios-font-family;
+    padding: ios.$ios-spacing-sm ios.$ios-spacing-md;
+
+    .info-text {
+      flex: 1;
+
+      .name {
+        font-size: 20px;
+        font-weight: 600;
+        color: ios.$ios-primary;
+        margin-bottom: 4px;
+      }
 
-      mat-icon {
-        font-size: 18px;
-        width: 18px;
-        height: 18px;
+      .position {
+        color: ios.$ios-primary;
+        font-weight: ios.$ios-font-weight-medium;
+        font-size: 14px;
       }
     }
   }
-}
 
-// 响应式调整
-@media (max-width: 768px) {
-  .hr-dialog {
-    .mat-mdc-dialog-container .mdc-dialog__surface {
-      min-width: 90vw;
-      max-width: 95vw;
-      margin: $ios-spacing-md;
-    }
+  .detail-grid {
+    display: grid;
+    grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
+    gap: ios.$ios-spacing-sm;
+    margin: ios.$ios-spacing-md;
 
-    .mat-mdc-dialog-content {
-      .form-row {
-        flex-direction: column;
-        
-        mat-form-field {
-          width: 100%;
-        }
+    .detail-item {
+      display: flex;
+      flex-direction: column;
+      padding: 12px;
+      background: #f8f9fa;
+      border-radius: 8px;
+
+      .label {
+        font-size: 12px;
+        color: #666;
+        margin-bottom: 4px;
+        text-transform: uppercase;
+        letter-spacing: 0.5px;
       }
 
-      .metrics-grid {
-        grid-template-columns: 1fr;
+      .value {
+        font-size: 14px;
+        color: #333;
+        font-weight: 500;
       }
     }
   }
-
-  .mat-mdc-select-panel {
-    max-height: 250px;
-    
-    .mat-mdc-option {
-      padding: $ios-spacing-md;
-    }
-  }
 }

+ 3 - 0
src/styles.scss

@@ -6,6 +6,9 @@
 // custom components at https://material.angular.dev/guide/theming
 @use '@angular/material' as mat;
 
+// 导入变量
+@import './styles/variables';
+
 // 自定义字体配置
 @include mat.core();
 

+ 45 - 0
src/styles/_variables.scss

@@ -0,0 +1,45 @@
+// iOS风格变量定义
+
+// 颜色
+$ios-primary: #007AFF;
+$ios-background: #FFFFFF;
+$ios-background-secondary: #F2F2F7;
+$ios-background-tertiary: #FFFFFF;
+$ios-border: #C6C6C8;
+$ios-text-primary: #000000;
+$ios-text-secondary: #8E8E93;
+$ios-text-tertiary: #C7C7CC;
+$ios-error: #FF3B30;
+
+// 字体大小
+$ios-font-size-xs: 12px;
+$ios-font-size-sm: 14px;
+$ios-font-size-md: 16px;
+$ios-font-size-lg: 18px;
+$ios-font-size-xl: 20px;
+
+// 字体粗细
+$ios-font-weight-light: 300;
+$ios-font-weight-normal: 400;
+$ios-font-weight-medium: 500;
+$ios-font-weight-semibold: 600;
+$ios-font-weight-bold: 700;
+
+// 间距
+$ios-spacing-xs: 4px;
+$ios-spacing-sm: 8px;
+$ios-spacing-md: 16px;
+$ios-spacing-lg: 24px;
+$ios-spacing-xl: 32px;
+
+// 圆角
+$ios-radius-sm: 4px;
+$ios-radius-md: 8px;
+$ios-radius-lg: 12px;
+$ios-radius-xl: 16px;
+
+// 兼容旧的变量名
+$ios-border-radius-sm: $ios-radius-sm;
+$ios-border-radius-md: $ios-radius-md;
+$ios-border-radius-lg: $ios-radius-lg;
+$ios-border-radius-xl: $ios-radius-xl;