Kaynağa Gözat

feat:team-leader-002

0235711 1 gün önce
ebeveyn
işleme
6289bd40d3

+ 105 - 59
src/app/pages/team-leader/dashboard/dashboard.html

@@ -1,5 +1,29 @@
 <header class="dashboard-header">
   <h1>设计组长工作台</h1>
+  <!-- 核心数据指标卡片 -->
+  <div class="dashboard-metrics">
+    <div class="metric-card" (click)="filterByStatus('overdue')">
+      <div class="metric-icon warning">⚠️</div>
+      <div class="metric-content">
+        <div class="metric-count">{{ overdueProjects.length }}</div>
+        <div class="metric-label">已延期项目</div>
+      </div>
+    </div>
+    <div class="metric-card" (click)="filterByStatus('pendingApproval')">
+      <div class="metric-icon info">📋</div>
+      <div class="metric-content">
+        <div class="metric-count">{{ pendingApprovalProjects.length }}</div>
+        <div class="metric-label">待组长确认项目</div>
+      </div>
+    </div>
+    <div class="metric-card" (click)="filterByStatus('pendingAssignment')">
+      <div class="metric-icon primary">🎯</div>
+      <div class="metric-content">
+        <div class="metric-count">{{ pendingAssignmentProjects.length }}</div>
+        <div class="metric-label">待分配方案项目</div>
+      </div>
+    </div>
+  </div>
 </header>
 
 <main class="dashboard-main">
@@ -8,79 +32,68 @@
     <div class="section-header">
       <h2>项目监控大盘</h2>
       <div class="section-filters">
-        <select (change)="filterProjects($event)">
+        <select (change)="filterProjects($event)" class="custom-select">
           <option value="all">全部项目</option>
           <option value="soft">软装项目</option>
           <option value="hard">硬装项目</option>
         </select>
-        <select (change)="filterByUrgency($event)">
+        <select (change)="filterByUrgency($event)" class="custom-select">
           <option value="all">全部紧急程度</option>
           <option value="high">高</option>
           <option value="medium">中</option>
           <option value="low">低</option>
         </select>
+        <select (change)="onStatusChange($event)" class="custom-select">
+          <option value="all">全部状态</option>
+          <option value="progress">进行中</option>
+          <option value="completed">已完成</option>
+          <option value="overdue">已延期</option>
+          <option value="pendingApproval">待确认</option>
+          <option value="pendingAssignment">待分配</option>
+        </select>
+        <!-- 支持数百项目的下拉筛选 -->
+        <select [(ngModel)]="selectedProjectId" (change)="selectProject()" class="custom-select project-selector">
+          <option value="">选择项目</option>
+          <option *ngFor="let project of projects" [value]="project.id">{{ project.name }}</option>
+        </select>
       </div>
     </div>
     
-    <div class="project-gantt-chart">
-      <div *ngFor="let project of projects" class="project-bar">
-        <div class="project-info">
-          <h3 [routerLink]="['/team-leader/project-review', project.id]" class="project-name">{{ project.name }}</h3>
-          <p class="project-details">负责组员: {{ project.designerName }} | 状态: {{ project.status }}</p>
-          <p [class.overdue-warning]="project.isOverdue" class="project-deadline">
-            {{ project.isOverdue ? '⚠️ 已超期' : '预计完成: ' + (project.expectedEndDate | date:'yyyy-MM-dd') }}
-          </p>
-        </div>
-        <div class="gantt-bars">
-          <div *ngFor="let phase of project.phases" 
-               [style.width]="phase.percentage + '%'"
-               [style.left]="phase.startPercentage + '%'"
-               [class]="'phase-bar phase-' + phase.name.toLowerCase()"
-               [class.completed]="phase.isCompleted"
-               [class.current]="phase.isCurrent">
-            <span class="phase-label">{{ phase.name }}</span>
-          </div>
+    <!-- 项目看板 - 横向展开10个项目阶段 -->
+    <div class="project-kanban">
+      <!-- 阶段标题 -->
+      <div class="kanban-header">
+        <div class="kanban-column-header" *ngFor="let stage of projectStages">
+          <h3>{{ stage.name }}</h3>
+          <span class="stage-count">{{ getProjectCountByStage(stage.id) }}</span>
         </div>
       </div>
-    </div>
-  </section>
-
-  <!-- 工作量评估快捷入口 -->
-  <section class="workload-section">
-    <div class="section-header">
-      <h2>工作量评估</h2>
-    </div>
-    
-    <div class="workload-calculator">
-      <div class="calculator-form">
-        <div class="form-group">
-          <label>户型面积 (㎡)</label>
-          <input type="number" [(ngModel)]="workloadParams.area" placeholder="请输入面积">
-        </div>
-        <div class="form-group">
-          <label>风格复杂度</label>
-          <select [(ngModel)]="workloadParams.styleComplexity">
-            <option value="simple">简单</option>
-            <option value="medium">中等</option>
-            <option value="complex">复杂</option>
-          </select>
-        </div>
-        <div class="form-group">
-          <label>交付要求</label>
-          <select [(ngModel)]="workloadParams.deliveryRequirement">
-            <option value="standard">标准</option>
-            <option value="premium">优质</option>
-            <option value="vip">VIP</option>
-          </select>
+      <!-- 项目卡片 -->
+      <div class="kanban-body">
+        <div class="kanban-column" *ngFor="let stage of projectStages">
+          <div *ngFor="let project of getProjectsByStage(stage.id)" 
+               class="project-card" 
+               [class.overdue]="project.isOverdue" 
+               [class.high-urgency]="project.urgency === 'high'">
+            <div class="project-card-header">
+              <h4 [routerLink]="['/team-leader/project-review', project.id]">{{ project.name }}</h4>
+              <span class="project-urgency" [class]="'urgency-' + project.urgency">{{ getUrgencyLabel(project.urgency) }}</span>
+            </div>
+            <div class="project-card-content">
+              <p>负责人: {{ project.designerName }}</p>
+              <p class="deadline">{{ project.isOverdue ? '超期' + project.overdueDays + '天' : '截止: ' + (project.expectedEndDate | date:'MM-dd') }}</p>
+            </div>
+            <div class="project-card-footer">
+              <button (click)="viewProjectDetails(project.id)" class="btn-view">查看详情</button>
+              <button (click)="quickAssignProject(project.id)" *ngIf="stage.id === 'pendingAssignment'" class="btn-assign">分配</button>
+            </div>
+          </div>
+          <!-- 空状态 -->
+          <div *ngIf="getProjectsByStage(stage.id).length === 0" class="empty-column">
+            <span class="empty-icon">📦</span>
+            <p>暂无项目</p>
+          </div>
         </div>
-        <button (click)="calculateWorkload()" class="btn-calculate">一键评估</button>
-      </div>
-      
-      <div *ngIf="workloadResult" class="workload-result">
-        <h3>评估结果</h3>
-        <p><strong>周期建议:</strong> 建模 {{ workloadResult.modelingDays }}天 + 渲染 {{ workloadResult.renderingDays }}天</p>
-        <p><strong>基础报价:</strong> 建模 {{ workloadResult.modelingPrice }}元/㎡、渲染 {{ workloadResult.renderingPrice }}元/张</p>
-        <button (click)="syncToProjectReview()" class="btn-sync">同步至项目评审</button>
       </div>
     </div>
   </section>
@@ -108,6 +121,36 @@
     </div>
   </section>
 
+  <!-- 快速操作面板 -->
+  <section class="quick-actions-section">
+    <div class="section-header">
+      <h2>快速操作</h2>
+    </div>
+    
+    <div class="quick-actions-grid">
+      <div class="action-card" (click)="navigateToTeamManagement()">
+        <div class="action-icon">👥</div>
+        <h3>团队管理</h3>
+        <p>查看组员能力和绩效</p>
+      </div>
+      <div class="action-card" (click)="navigateToProjectReview()">
+        <div class="action-icon">📊</div>
+        <h3>项目评审</h3>
+        <p>审核和分配新项目</p>
+      </div>
+      <div class="action-card" (click)="navigateToQualityManagement()">
+        <div class="action-icon">✅</div>
+        <h3>质量管理</h3>
+        <p>查看整改任务和标准</p>
+      </div>
+      <div class="action-card" (click)="openWorkloadEstimator()">
+        <div class="action-icon">⏱️</div>
+        <h3>工作量预估</h3>
+        <p>前往项目详情页使用</p>
+      </div>
+    </div>
+  </section>
+
   <!-- 超期项目提醒 -->
   <div *ngIf="showAlert && overdueProjects.length > 0" class="overdue-alert">
     <div class="alert-content">
@@ -117,7 +160,10 @@
           {{ project.name }} ({{ project.designerName }} 负责) - 超期{{ project.overdueDays }}天
         </li>
       </ul>
-      <button (click)="closeAlert()" class="btn-close">关闭</button>
+      <div class="alert-actions">
+        <button (click)="viewAllOverdueProjects()" class="btn-view-all">查看全部</button>
+        <button (click)="closeAlert()" class="btn-close">关闭</button>
+      </div>
     </div>
   </div>
 </main>

+ 406 - 161
src/app/pages/team-leader/dashboard/dashboard.scss

@@ -13,7 +13,74 @@
     font-size: $ios-font-size-xl;
     font-weight: $ios-font-weight-bold;
     color: $ios-text-primary;
-    margin: 0;
+    margin: 0 0 $ios-spacing-lg 0;
+  }
+  
+  // 核心数据指标卡片样式
+  .dashboard-metrics {
+    display: grid;
+    grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
+    gap: $ios-spacing-lg;
+    margin-top: $ios-spacing-lg;
+    
+    .metric-card {
+      display: flex;
+      align-items: center;
+      gap: $ios-spacing-md;
+      background: linear-gradient(135deg, $ios-card-background, #f8f9fa);
+      border-radius: $ios-radius-lg;
+      padding: $ios-spacing-lg;
+      border: 1px solid $ios-border;
+      box-shadow: $ios-shadow-sm;
+      cursor: pointer;
+      transition: all 0.3s ease;
+      
+      &:hover {
+        transform: translateY(-2px);
+        box-shadow: $ios-shadow-card;
+      }
+      
+      .metric-icon {
+        font-size: 2rem;
+        width: 50px;
+        height: 50px;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        border-radius: $ios-radius-full;
+        background-color: $ios-background;
+      }
+      
+      .metric-icon.warning {
+        background-color: rgba(255, 149, 0, 0.1);
+      }
+      
+      .metric-icon.info {
+        background-color: rgba(59, 130, 246, 0.1);
+      }
+      
+      .metric-icon.primary {
+        background-color: rgba(124, 58, 237, 0.1);
+      }
+      
+      .metric-content {
+        flex: 1;
+      }
+      
+      .metric-count {
+        font-size: 2rem;
+        font-weight: $ios-font-weight-bold;
+        color: $ios-text-primary;
+        line-height: 1.2;
+      }
+      
+      .metric-label {
+        font-size: $ios-font-size-sm;
+        color: $ios-text-secondary;
+        text-transform: uppercase;
+        letter-spacing: 0.5px;
+      }
+    }
   }
 }
 
@@ -38,7 +105,9 @@
 .section-filters {
   display: flex;
   gap: $ios-spacing-md;
-  select {
+  
+  // 自定义选择框样式
+  .custom-select {
     padding: $ios-spacing-sm $ios-spacing-md;
     border: 1px solid $ios-border;
     border-radius: $ios-radius-md;
@@ -46,9 +115,18 @@
     font-size: $ios-font-size-sm;
     color: $ios-text-primary;
     cursor: pointer;
+    transition: all 0.2s ease;
+    
     &:focus {
       outline: none;
       border-color: $ios-primary;
+      box-shadow: 0 0 0 2px rgba(124, 58, 237, 0.1);
+    }
+    
+    // 支持数百项目的下拉筛选器
+    &.project-selector {
+      min-width: 200px;
+      max-height: 40px;
     }
   }
 }
@@ -60,182 +138,293 @@
   padding: $ios-spacing-xl;
   margin-bottom: $ios-spacing-xl;
   box-shadow: $ios-shadow-card;
+  position: relative;
+  overflow: hidden;
+  
+  // 科技感背景元素
+  &::before {
+    content: '';
+    position: absolute;
+    top: 0;
+    right: 0;
+    width: 300px;
+    height: 300px;
+    background: linear-gradient(135deg, rgba(124, 58, 237, 0.05), transparent);
+    border-radius: 50%;
+    transform: translate(50%, -50%);
+    z-index: 0;
+  }
 }
 
-.project-gantt-chart {
-  .project-bar {
+/* 项目看板样式 - 横向展开10个项目阶段 */
+.project-kanban {
+  position: relative;
+  z-index: 1;
+  
+  // 看板标题行
+  .kanban-header {
     display: flex;
-    gap: $ios-spacing-lg;
-    padding: $ios-spacing-lg 0;
-    border-bottom: 1px solid $ios-border;
-    &:last-child {
-      border-bottom: none;
-    }
-    .project-info {
-      flex: 0 0 300px;
-      .project-name {
-        font-size: $ios-font-size-base;
-        font-weight: $ios-font-weight-medium;
-        color: $ios-primary;
-        cursor: pointer;
-        margin: 0 0 $ios-spacing-xs 0;
-        &:hover {
-          text-decoration: underline;
-        }
-      }
-      .project-details {
-        font-size: $ios-font-size-sm;
-        color: $ios-text-secondary;
-        margin: 0 0 $ios-spacing-xs 0;
-      }
-      .project-deadline {
+    gap: $ios-spacing-md;
+    margin-bottom: $ios-spacing-md;
+    
+    .kanban-column-header {
+      flex: 1;
+      min-width: 180px;
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      padding: $ios-spacing-sm $ios-spacing-md;
+      background-color: $ios-background;
+      border-radius: $ios-radius-md;
+      border: 1px solid $ios-border;
+      
+      h3 {
         font-size: $ios-font-size-sm;
-        color: $ios-text-secondary;
+        font-weight: $ios-font-weight-medium;
+        color: $ios-text-primary;
         margin: 0;
-        &.overdue-warning {
-          color: $ios-danger;
-          font-weight: $ios-font-weight-medium;
-        }
+      }
+      
+      .stage-count {
+        font-size: $ios-font-size-xs;
+        background-color: $ios-primary;
+        color: $ios-background;
+        padding: 2px 8px;
+        border-radius: $ios-radius-full;
+        font-weight: $ios-font-weight-medium;
       }
     }
-    .gantt-bars {
+  }
+  
+  // 看板主体内容
+  .kanban-body {
+    display: flex;
+    gap: $ios-spacing-md;
+    overflow-x: auto;
+    padding-bottom: $ios-spacing-md;
+    
+    // 滚动条样式
+    &::-webkit-scrollbar {
+      height: 6px;
+    }
+    
+    &::-webkit-scrollbar-track {
+      background: $ios-background;
+      border-radius: $ios-radius-full;
+    }
+    
+    &::-webkit-scrollbar-thumb {
+      background: $ios-border;
+      border-radius: $ios-radius-full;
+    }
+    
+    &::-webkit-scrollbar-thumb:hover {
+      background: $ios-text-tertiary;
+    }
+    
+    // 看板列
+    .kanban-column {
       flex: 1;
-      position: relative;
-      height: 40px;
-      background-color: $ios-background-secondary;
+      min-width: 180px;
+      max-width: 180px;
+      height: 400px;
+      background-color: $ios-background;
       border-radius: $ios-radius-md;
-      overflow: hidden;
-      .phase-bar {
-        position: absolute;
-        top: 50%;
-        transform: translateY(-50%);
-        height: 24px;
-        border-radius: $ios-radius-sm;
-        display: flex;
-        align-items: center;
-        justify-content: center;
-        transition: $ios-feedback-hover;
-        .phase-label {
-          font-size: $ios-font-size-xs;
-          color: $ios-background;
-          font-weight: $ios-font-weight-medium;
-          text-shadow: 0 1px 2px rgba(0,0,0,0.2);
-        }
-      }
-      .phase-建模 {
-        background-color: $ios-primary-light;
+      border: 1px solid $ios-border;
+      padding: $ios-spacing-sm;
+      overflow-y: auto;
+      
+      // 滚动条样式
+      &::-webkit-scrollbar {
+        width: 4px;
       }
-      .phase-渲染 {
-        background-color: $ios-info;
+      
+      &::-webkit-scrollbar-track {
+        background: transparent;
       }
-      .phase-后期 {
-        background-color: $ios-warning;
-      }
-      .phase-交付 {
-        background-color: $ios-success;
+      
+      &::-webkit-scrollbar-thumb {
+        background: $ios-border;
+        border-radius: $ios-radius-full;
       }
-      .phase-bar.current {
-        box-shadow: 0 0 0 2px $ios-primary;
-        transform: translateY(-50%) scale(1.05);
+      
+      // 项目卡片
+      .project-card {
+        background-color: $ios-card-background;
+        border-radius: $ios-radius-md;
+        padding: $ios-spacing-md;
+        margin-bottom: $ios-spacing-sm;
+        border: 1px solid $ios-border;
+        box-shadow: $ios-shadow-sm;
+        transition: all 0.2s ease;
+        
+        &:hover {
+          transform: translateY(-2px);
+          box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
+        }
+        
+        &.overdue {
+          border-left: 4px solid $ios-danger;
+        }
+        
+        &.high-urgency {
+          border-left: 4px solid $ios-warning;
+        }
+        
+        .project-card-header {
+          display: flex;
+          justify-content: space-between;
+          align-items: flex-start;
+          margin-bottom: $ios-spacing-sm;
+          
+          h4 {
+            font-size: $ios-font-size-sm;
+            font-weight: $ios-font-weight-medium;
+            color: $ios-primary;
+            margin: 0;
+            cursor: pointer;
+            &:hover {
+              text-decoration: underline;
+            }
+          }
+          
+          .project-urgency {
+            font-size: 10px;
+            padding: 2px 6px;
+            border-radius: $ios-radius-full;
+            font-weight: $ios-font-weight-medium;
+          }
+          
+          .urgency-high {
+            background-color: rgba(239, 68, 68, 0.1);
+            color: $ios-danger;
+          }
+          
+          .urgency-medium {
+            background-color: rgba(255, 149, 0, 0.1);
+            color: $ios-warning;
+          }
+          
+          .urgency-low {
+            background-color: rgba(59, 130, 246, 0.1);
+            color: $ios-info;
+          }
+        }
+        
+        .project-card-content {
+          margin-bottom: $ios-spacing-sm;
+          
+          p {
+            font-size: 11px;
+            color: $ios-text-secondary;
+            margin: 0 0 4px 0;
+          }
+          
+          .deadline {
+            font-size: 10px;
+            color: $ios-text-tertiary;
+          }
+        }
+        
+        .project-card-footer {
+          display: flex;
+          gap: 4px;
+          
+          button {
+            flex: 1;
+            font-size: 10px;
+            padding: 4px 6px;
+            border: none;
+            border-radius: $ios-radius-sm;
+            cursor: pointer;
+            transition: all 0.2s ease;
+          }
+          
+          .btn-view {
+            background-color: $ios-primary;
+            color: $ios-background;
+          }
+          
+          .btn-assign {
+            background-color: $ios-success;
+            color: $ios-background;
+          }
+          
+          button:hover {
+            opacity: 0.9;
+          }
+        }
       }
-      .phase-bar.completed {
-        opacity: 0.6;
+      
+      // 空状态
+      .empty-column {
+        display: flex;
+        flex-direction: column;
+        align-items: center;
+        justify-content: center;
+        height: 100%;
+        
+        .empty-icon {
+          font-size: 2rem;
+          margin-bottom: $ios-spacing-sm;
+          opacity: 0.3;
+        }
+        
+        p {
+          font-size: $ios-font-size-sm;
+          color: $ios-text-tertiary;
+          margin: 0;
+        }
       }
     }
   }
 }
 
-/* 工作量评估样式 */
-.workload-section {
+/* 快速操作面板样式 */
+.quick-actions-section {
   background-color: $ios-card-background;
   border-radius: $ios-radius-lg;
   padding: $ios-spacing-xl;
   margin-bottom: $ios-spacing-xl;
   box-shadow: $ios-shadow-card;
-}
-
-.workload-calculator {
-  display: flex;
-  gap: $ios-spacing-xl;
-  .calculator-form {
-    flex: 1;
-    display: flex;
-    flex-direction: column;
+  
+  .quick-actions-grid {
+    display: grid;
+    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
     gap: $ios-spacing-lg;
-    .form-group {
-      display: flex;
-      flex-direction: column;
-      gap: $ios-spacing-sm;
-      label {
-        font-size: $ios-font-size-sm;
-        font-weight: $ios-font-weight-medium;
-        color: $ios-text-primary;
-      }
-      input,
-      select {
-        padding: $ios-spacing-sm $ios-spacing-md;
-        border: 1px solid $ios-border;
-        border-radius: $ios-radius-md;
-        background-color: $ios-background;
-        font-size: $ios-font-size-base;
-        color: $ios-text-primary;
-        &:focus {
-          outline: none;
-          border-color: $ios-primary;
-        }
-      }
-    }
-    .btn-calculate {
-      background-color: $ios-primary;
-      color: $ios-background;
-      border: none;
-      border-radius: $ios-radius-md;
-      padding: $ios-spacing-md;
-      font-size: $ios-font-size-base;
-      font-weight: $ios-font-weight-medium;
+    
+    .action-card {
+      background: linear-gradient(135deg, $ios-background, #f8f9fa);
+      border-radius: $ios-radius-lg;
+      padding: $ios-spacing-xl;
+      border: 1px solid $ios-border;
+      text-align: center;
       cursor: pointer;
-      transition: $ios-feedback-tap;
+      transition: all 0.3s ease;
+      
       &:hover {
-        background-color: $ios-primary-light;
-        transform: translateY(-1px);
+        transform: translateY(-3px);
+        box-shadow: 0 8px 24px rgba(0, 0, 0, 0.08);
+        border-color: $ios-primary-light;
       }
-      &:active {
-        transform: translateY(0);
+      
+      .action-icon {
+        font-size: 2.5rem;
+        margin-bottom: $ios-spacing-md;
+        opacity: 0.8;
       }
-    }
-  }
-  .workload-result {
-    flex: 1;
-    background-color: $ios-background-secondary;
-    border-radius: $ios-radius-md;
-    padding: $ios-spacing-lg;
-    h3 {
-      font-size: $ios-font-size-base;
-      font-weight: $ios-font-weight-medium;
-      color: $ios-text-primary;
-      margin: 0 0 $ios-spacing-lg 0;
-    }
-    p {
-      margin: 0 0 $ios-spacing-md 0;
-      font-size: $ios-font-size-base;
-      color: $ios-text-primary;
-      strong {
+      
+      h3 {
+        font-size: $ios-font-size-base;
         font-weight: $ios-font-weight-medium;
+        color: $ios-text-primary;
+        margin: 0 0 $ios-spacing-xs 0;
       }
-    }
-    .btn-sync {
-      background-color: $ios-success;
-      color: $ios-background;
-      border: none;
-      border-radius: $ios-radius-md;
-      padding: $ios-spacing-sm $ios-spacing-lg;
-      font-size: $ios-font-size-sm;
-      font-weight: $ios-font-weight-medium;
-      cursor: pointer;
-      transition: $ios-feedback-tap;
-      &:hover {
-        background-color: #2AA049;
-        transform: translateY(-1px);
+      
+      p {
+        font-size: $ios-font-size-sm;
+        color: $ios-text-secondary;
+        margin: 0;
       }
     }
   }
@@ -258,33 +447,41 @@
     background-color: $ios-background;
     border: 1px solid $ios-border;
     transition: $ios-feedback-hover;
+    
     &:last-child {
       margin-bottom: 0;
     }
+    
     &.priority-high {
       border-left: 4px solid $ios-danger;
     }
+    
     &.priority-medium {
       border-left: 4px solid $ios-warning;
     }
+    
     &.priority-low {
       border-left: 4px solid $ios-info;
     }
+    
     &:hover {
       transform: translateY(-1px);
       box-shadow: $ios-shadow-sm;
     }
+    
     .todo-header {
       display: flex;
       justify-content: space-between;
       align-items: center;
       margin-bottom: $ios-spacing-md;
+      
       h3 {
         font-size: $ios-font-size-base;
         font-weight: $ios-font-weight-medium;
         color: $ios-text-primary;
         margin: 0;
       }
+      
       .task-priority {
         font-size: $ios-font-size-xs;
         padding: $ios-spacing-xs $ios-spacing-sm;
@@ -292,18 +489,22 @@
         font-weight: $ios-font-weight-medium;
       }
     }
+    
     .todo-info {
       margin-bottom: $ios-spacing-md;
+      
       p {
         margin: 0 0 $ios-spacing-xs 0;
         font-size: $ios-font-size-sm;
         color: $ios-text-secondary;
       }
+      
       .task-deadline {
         font-size: $ios-font-size-xs;
         color: $ios-text-tertiary;
       }
     }
+    
     .todo-actions {
       .btn-handle {
         background-color: $ios-primary;
@@ -315,6 +516,7 @@
         font-weight: $ios-font-weight-medium;
         cursor: pointer;
         transition: $ios-feedback-tap;
+        
         &:hover {
           background-color: $ios-primary-light;
         }
@@ -332,41 +534,84 @@
   background-color: $ios-card-background;
   border-radius: $ios-radius-lg;
   padding: $ios-spacing-xl;
-  box-shadow: $ios-shadow-lg;
+  box-shadow: 0 10px 40px rgba(0, 0, 0, 0.15);
   z-index: 1000;
   min-width: 400px;
+  animation: slideIn 0.3s ease-out;
+  
+  @keyframes slideIn {
+    from {
+      opacity: 0;
+      transform: translate(-50%, -60%);
+    }
+    to {
+      opacity: 1;
+      transform: translate(-50%, -50%);
+    }
+  }
+  
   .alert-content {
     h3 {
       font-size: $ios-font-size-lg;
       font-weight: $ios-font-weight-semibold;
       color: $ios-danger;
       margin: 0 0 $ios-spacing-lg 0;
+      display: flex;
+      align-items: center;
+      gap: $ios-spacing-sm;
     }
+    
     ul {
       margin: 0 0 $ios-spacing-lg 0;
       padding-left: $ios-spacing-xl;
+      
       li {
         font-size: $ios-font-size-base;
         color: $ios-text-primary;
         margin-bottom: $ios-spacing-sm;
+        
         &:last-child {
           margin-bottom: 0;
         }
       }
     }
-    .btn-close {
-      background-color: $ios-text-tertiary;
-      color: $ios-text-primary;
-      border: none;
-      border-radius: $ios-radius-md;
-      padding: $ios-spacing-sm $ios-spacing-lg;
-      font-size: $ios-font-size-sm;
-      font-weight: $ios-font-weight-medium;
-      cursor: pointer;
-      transition: $ios-feedback-tap;
-      &:hover {
-        background-color: $ios-text-secondary;
+    
+    .alert-actions {
+      display: flex;
+      gap: $ios-spacing-md;
+      justify-content: flex-end;
+      
+      .btn-view-all {
+        background-color: $ios-primary;
         color: $ios-background;
+        border: none;
+        border-radius: $ios-radius-md;
+        padding: $ios-spacing-sm $ios-spacing-lg;
+        font-size: $ios-font-size-sm;
+        font-weight: $ios-font-weight-medium;
+        cursor: pointer;
+        transition: $ios-feedback-tap;
+        
+        &:hover {
+          background-color: $ios-primary-light;
+        }
+      }
+      
+      .btn-close {
+        background-color: $ios-text-tertiary;
+        color: $ios-text-primary;
+        border: none;
+        border-radius: $ios-radius-md;
+        padding: $ios-spacing-sm $ios-spacing-lg;
+        font-size: $ios-font-size-sm;
+        font-weight: $ios-font-weight-medium;
+        cursor: pointer;
+        transition: $ios-feedback-tap;
+        
+        &:hover {
+          background-color: $ios-text-secondary;
+          color: $ios-background;
+        }
       }
     }
   }

+ 164 - 84
src/app/pages/team-leader/dashboard/dashboard.ts

@@ -1,9 +1,16 @@
-import { Component, OnInit } from '@angular/core';
 import { CommonModule } from '@angular/common';
 import { FormsModule } from '@angular/forms';
 import { Router, RouterModule } from '@angular/router';
+import { Component, OnInit } from '@angular/core';
 import { ProjectService } from '../../../services/project.service';
 
+// 项目阶段定义
+interface ProjectStage {
+  id: string;
+  name: string;
+  order: number;
+}
+
 interface ProjectPhase {
   name: string;
   percentage: number;
@@ -23,19 +30,7 @@ interface Project {
   overdueDays: number;
   urgency: 'high' | 'medium' | 'low';
   phases: ProjectPhase[];
-}
-
-interface WorkloadParams {
-  area: number;
-  styleComplexity: 'simple' | 'medium' | 'complex';
-  deliveryRequirement: 'standard' | 'premium' | 'vip';
-}
-
-interface WorkloadResult {
-  modelingDays: number;
-  renderingDays: number;
-  modelingPrice: number;
-  renderingPrice: number;
+  currentStage: string; // 新增:当前项目阶段
 }
 
 interface TodoTask {
@@ -58,15 +53,24 @@ interface TodoTask {
 export class Dashboard implements OnInit {
   projects: Project[] = [];
   filteredProjects: Project[] = [];
-  workloadParams: WorkloadParams = {
-    area: 0,
-    styleComplexity: 'medium',
-    deliveryRequirement: 'standard'
-  };
-  workloadResult: WorkloadResult | null = null;
   todoTasks: TodoTask[] = [];
   overdueProjects: Project[] = [];
   showAlert: boolean = false;
+  selectedProjectId: string = '';
+  
+  // 定义10个项目阶段
+  projectStages: ProjectStage[] = [
+    { id: 'pendingApproval', name: '待确认', order: 1 },
+    { id: 'pendingAssignment', name: '待分配', order: 2 },
+    { id: 'requirement', name: '需求沟通', order: 3 },
+    { id: 'planning', name: '方案规划', order: 4 },
+    { id: 'modeling', name: '建模阶段', order: 5 },
+    { id: 'rendering', name: '渲染阶段', order: 6 },
+    { id: 'postProduction', name: '后期处理', order: 7 },
+    { id: 'review', name: '方案评审', order: 8 },
+    { id: 'revision', name: '方案修改', order: 9 },
+    { id: 'delivery', name: '交付完成', order: 10 }
+  ];
 
   constructor(private projectService: ProjectService, private router: Router) {}
 
@@ -76,7 +80,7 @@ export class Dashboard implements OnInit {
   }
 
   loadProjects(): void {
-    // 模拟数据加载
+    // 模拟数据加载 - 增强数据结构,添加currentStage
     this.projects = [
       {
         id: 'proj-001',
@@ -88,6 +92,7 @@ export class Dashboard implements OnInit {
         isOverdue: true,
         overdueDays: 2,
         urgency: 'high',
+        currentStage: 'rendering',
         phases: [
           { name: '建模', percentage: 15, startPercentage: 0, isCompleted: true, isCurrent: false },
           { name: '渲染', percentage: 20, startPercentage: 15, isCompleted: false, isCurrent: true },
@@ -105,6 +110,7 @@ export class Dashboard implements OnInit {
         isOverdue: false,
         overdueDays: 0,
         urgency: 'medium',
+        currentStage: 'postProduction',
         phases: [
           { name: '建模', percentage: 15, startPercentage: 0, isCompleted: true, isCurrent: false },
           { name: '渲染', percentage: 20, startPercentage: 15, isCompleted: true, isCurrent: false },
@@ -122,6 +128,7 @@ export class Dashboard implements OnInit {
         isOverdue: false,
         overdueDays: 0,
         urgency: 'low',
+        currentStage: 'modeling',
         phases: [
           { name: '建模', percentage: 15, startPercentage: 0, isCompleted: false, isCurrent: true },
           { name: '渲染', percentage: 20, startPercentage: 15, isCompleted: false, isCurrent: false },
@@ -139,12 +146,53 @@ export class Dashboard implements OnInit {
         isOverdue: true,
         overdueDays: 7,
         urgency: 'high',
+        currentStage: 'review',
         phases: [
           { name: '建模', percentage: 15, startPercentage: 0, isCompleted: true, isCurrent: false },
           { name: '渲染', percentage: 20, startPercentage: 15, isCompleted: true, isCurrent: false },
           { name: '后期', percentage: 15, startPercentage: 35, isCompleted: false, isCurrent: false },
           { name: '交付', percentage: 50, startPercentage: 50, isCompleted: false, isCurrent: false }
         ]
+      },
+      // 添加更多不同阶段的项目
+      {
+        id: 'proj-005',
+        name: '现代简约厨房设计',
+        type: 'soft',
+        designerName: '',
+        status: '待分配',
+        expectedEndDate: new Date(2023, 10, 5),
+        isOverdue: false,
+        overdueDays: 0,
+        urgency: 'medium',
+        currentStage: 'pendingAssignment',
+        phases: []
+      },
+      {
+        id: 'proj-006',
+        name: '日式风格书房设计',
+        type: 'hard',
+        designerName: '',
+        status: '待确认',
+        expectedEndDate: new Date(2023, 10, 10),
+        isOverdue: false,
+        overdueDays: 0,
+        urgency: 'low',
+        currentStage: 'pendingApproval',
+        phases: []
+      },
+      {
+        id: 'proj-007',
+        name: '轻奢风格浴室设计',
+        type: 'soft',
+        designerName: '钱七',
+        status: '已完成',
+        expectedEndDate: new Date(2023, 9, 5),
+        isOverdue: false,
+        overdueDays: 0,
+        urgency: 'medium',
+        currentStage: 'delivery',
+        phases: []
       }
     ];
 
@@ -219,6 +267,7 @@ export class Dashboard implements OnInit {
     });
   }
 
+  // 筛选项目类型
   filterProjects(event: Event): void {
     const target = event.target as HTMLSelectElement;
     const filterValue = target.value;
@@ -230,6 +279,7 @@ export class Dashboard implements OnInit {
     }
   }
 
+  // 筛选紧急程度
   filterByUrgency(event: Event): void {
     const target = event.target as HTMLSelectElement;
     const filterValue = target.value;
@@ -241,80 +291,71 @@ export class Dashboard implements OnInit {
     }
   }
 
-  calculateWorkload(): void {
-    if (!this.workloadParams.area || this.workloadParams.area <= 0) {
-      alert('请输入有效的户型面积');
-      return;
-    }
-
-    // 模拟工作量计算逻辑
-    let modelingDays = 1;
-    let renderingDays = 1;
-    let modelingPrice = 50;
-    let renderingPrice = 200;
-
-    // 根据面积调整
-    if (this.workloadParams.area > 100) {
-      modelingDays += 1;
-      renderingDays += 0.5;
+  // 筛选项目状态
+  filterByStatus(status: string): void {
+    if (status === 'all') {
+      this.filteredProjects = [...this.projects];
+    } else if (status === 'overdue') {
+      this.filteredProjects = this.overdueProjects;
+    } else if (status === 'pendingApproval') {
+      this.filteredProjects = this.projects.filter(project => project.currentStage === 'pendingApproval');
+    } else if (status === 'pendingAssignment') {
+      this.filteredProjects = this.projects.filter(project => project.currentStage === 'pendingAssignment');
+    } else if (status === 'progress') {
+      this.filteredProjects = this.projects.filter(project => 
+        ['requirement', 'planning', 'modeling', 'rendering', 'postProduction', 'review', 'revision'].includes(project.currentStage)
+      );
+    } else if (status === 'completed') {
+      this.filteredProjects = this.projects.filter(project => project.currentStage === 'delivery');
     }
-    if (this.workloadParams.area > 200) {
-      modelingDays += 1;
-      renderingDays += 0.5;
+  }
+  
+  // 处理状态筛选下拉框变化
+  onStatusChange(event: Event): void {
+    const target = event.target as HTMLSelectElement;
+    if (target && target.value) {
+      this.filterByStatus(target.value);
     }
+  }
 
-    // 根据风格复杂度调整
-    if (this.workloadParams.styleComplexity === 'medium') {
-      modelingDays += 1;
-      renderingDays += 0.5;
-      modelingPrice += 20;
-      renderingPrice += 50;
-    } else if (this.workloadParams.styleComplexity === 'complex') {
-      modelingDays += 2;
-      renderingDays += 1;
-      modelingPrice += 40;
-      renderingPrice += 100;
+  // 选择单个项目
+  selectProject(): void {
+    if (this.selectedProjectId) {
+      this.router.navigate(['/team-leader/project-review', this.selectedProjectId]);
     }
+  }
 
-    // 根据交付要求调整
-    if (this.workloadParams.deliveryRequirement === 'premium') {
-      modelingDays += 0.5;
-      renderingDays += 0.5;
-      modelingPrice += 30;
-      renderingPrice += 100;
-    } else if (this.workloadParams.deliveryRequirement === 'vip') {
-      modelingDays += 1;
-      renderingDays += 1;
-      modelingPrice += 50;
-      renderingPrice += 200;
-    }
+  // 获取特定阶段的项目
+  getProjectsByStage(stageId: string): Project[] {
+    return this.filteredProjects.filter(project => project.currentStage === stageId);
+  }
+
+  // 获取特定阶段的项目数量
+  getProjectCountByStage(stageId: string): number {
+    return this.getProjectsByStage(stageId).length;
+  }
 
-    this.workloadResult = {
-      modelingDays,
-      renderingDays,
-      modelingPrice,
-      renderingPrice
+  // 获取紧急程度标签
+  getUrgencyLabel(urgency: string): string {
+    const labels = {
+      high: '紧急',
+      medium: '一般',
+      low: '普通'
     };
+    return labels[urgency as keyof typeof labels] || urgency;
   }
 
-  syncToProjectReview(): void {
-    if (!this.workloadResult) return;
-
-    // 在实际应用中,这里会调用API将评估结果同步到project-review板块
-    // 这里我们直接跳转到project-review页面
-    this.router.navigate(['/team-leader/project-review'], {
-      queryParams: {
-        modelingDays: this.workloadResult.modelingDays,
-        renderingDays: this.workloadResult.renderingDays,
-        modelingPrice: this.workloadResult.modelingPrice,
-        renderingPrice: this.workloadResult.renderingPrice,
-        area: this.workloadParams.area,
-        style: this.workloadParams.styleComplexity,
-        delivery: this.workloadParams.deliveryRequirement
-      }
-    });
+  // 查看项目详情
+  viewProjectDetails(projectId: string): void {
+    this.router.navigate(['/team-leader/project-review', projectId]);
+  }
+
+  // 快速分配项目
+  quickAssignProject(projectId: string): void {
+    this.router.navigate(['/team-leader/project-review', projectId, 'assign']);
   }
 
+  // 导航到待办任务
   navigateToTask(task: TodoTask): void {
     switch (task.type) {
       case 'review':
@@ -329,6 +370,7 @@ export class Dashboard implements OnInit {
     }
   }
 
+  // 获取优先级标签
   getPriorityLabel(priority: 'high' | 'medium' | 'low'): string {
     const labels: Record<'high' | 'medium' | 'low', string> = {
       'high': '紧急且重要',
@@ -338,7 +380,45 @@ export class Dashboard implements OnInit {
     return labels[priority];
   }
 
+  // 导航到团队管理
+  navigateToTeamManagement(): void {
+    this.router.navigate(['/team-leader/team-management']);
+  }
+
+  // 导航到项目评审
+  navigateToProjectReview(): void {
+    this.router.navigate(['/team-leader/project-review']);
+  }
+
+  // 导航到质量管理
+  navigateToQualityManagement(): void {
+    this.router.navigate(['/team-leader/quality-management']);
+  }
+
+  // 打开工作量预估工具(已迁移)
+  openWorkloadEstimator(): void {
+    this.router.navigate(['/team-leader/project-review']);
+    alert('工作量预估工具已迁移至项目详情页,您可以在建模阶段之前使用该工具进行工作量计算。');
+  }
+
+  // 查看所有超期项目
+  viewAllOverdueProjects(): void {
+    this.filterByStatus('overdue');
+    this.closeAlert();
+  }
+
+  // 关闭提醒
   closeAlert(): void {
     this.showAlert = false;
   }
+
+  // 获取待确认项目数量
+  get pendingApprovalProjects() {
+    return this.projects.filter(project => project.currentStage === 'pendingApproval');
+  }
+
+  // 获取待分配项目数量
+  get pendingAssignmentProjects() {
+    return this.projects.filter(project => project.currentStage === 'pendingAssignment');
+  }
 }

+ 78 - 0
src/app/pages/team-leader/project-review/project-review.html

@@ -88,6 +88,84 @@
     </div>
   </section>
 
+  <!-- 工作量预估工具 -->
+  <section class="workload-estimation-section">
+    <div class="section-header">
+      <h2>工作量预估工具</h2>
+      <button (click)="toggleWorkloadEstimator()" class="btn-toggle">
+        {{ showWorkloadEstimator ? '收起' : '展开' }}
+      </button>
+    </div>
+    
+    <div *ngIf="showWorkloadEstimator" class="workload-estimation-form">
+      <div class="form-group">
+        <label>项目面积(㎡)</label>
+        <input type="number" [(ngModel)]="workloadEstimationForm.area" placeholder="请输入项目面积" min="1">
+      </div>
+      
+      <div class="form-group">
+        <label>项目复杂度</label>
+        <select [(ngModel)]="workloadEstimationForm.complexity">
+          <option value="low">低</option>
+          <option value="medium">中</option>
+          <option value="high">高</option>
+        </select>
+      </div>
+      
+      <div class="form-group">
+        <label>交付要求</label>
+        <select [(ngModel)]="workloadEstimationForm.deliveryRequirements">
+          <option value="standard">标准</option>
+          <option value="premium">优质</option>
+          <option value="vip">VIP</option>
+        </select>
+      </div>
+      
+      <button (click)="calculateWorkloadEstimation()" class="btn-calculate" [disabled]="!workloadEstimationForm.area || workloadEstimationForm.area <= 0">
+        计算工作量
+      </button>
+    </div>
+    
+    <!-- 工作量预估结果 -->
+    <div *ngIf="workloadEstimationResult" class="workload-estimation-result">
+      <div class="result-header">
+        <h3>工作量预估结果</h3>
+      </div>
+      
+      <div class="result-content">
+        <div class="total-workload">
+          <p class="total-label">总工时</p>
+          <p class="total-value">{{ formatWorkloadHours(workloadEstimationResult.totalHours) }}</p>
+        </div>
+        
+        <div class="workload-breakdown">
+          <h4>工时分配</h4>
+          <div class="breakdown-items">
+            <div class="breakdown-item">
+              <span class="item-label">建模阶段</span>
+              <span class="item-value">{{ formatWorkloadHours(workloadEstimationResult.breakdown.modeling) }}</span>
+            </div>
+            <div class="breakdown-item">
+              <span class="item-label">渲染阶段</span>
+              <span class="item-value">{{ formatWorkloadHours(workloadEstimationResult.breakdown.rendering) }}</span>
+            </div>
+            <div class="breakdown-item">
+              <span class="item-label">后期阶段</span>
+              <span class="item-value">{{ formatWorkloadHours(workloadEstimationResult.breakdown.postProduction) }}</span>
+            </div>
+          </div>
+        </div>
+        
+        <div class="workload-suggestions">
+          <h4>项目建议</h4>
+          <ul>
+            <li *ngFor="let suggestion of workloadEstimationResult.suggestions">{{ suggestion }}</li>
+          </ul>
+        </div>
+      </div>
+    </div>
+  </section>
+
   <!-- 项目分配审核与执行 -->
   <section class="assignment-section">
     <div class="section-header">

+ 176 - 1
src/app/pages/team-leader/project-review/project-review.scss

@@ -555,6 +555,171 @@
   }
 }
 
+/* 工作量预估工具样式 */
+.workload-estimation-section {
+  background-color: white;
+  border-radius: $ios-radius-lg;
+  padding: 20px;
+  margin-bottom: 20px;
+  box-shadow: $ios-shadow-card;
+}
+
+.workload-estimation-section .section-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+}
+
+.btn-toggle {
+  background-color: $ios-background-secondary;
+  color: $ios-text-primary;
+  border: 1px solid $ios-border;
+  border-radius: $ios-radius-lg;
+  padding: 8px 16px;
+  font-size: 14px;
+  cursor: pointer;
+  transition: all 0.3s ease;
+  &:hover {
+    background-color: $ios-background-tertiary;
+  }
+}
+
+.workload-estimation-form {
+  .form-group {
+    margin-bottom: 15px;
+    label {
+      display: block;
+      margin-bottom: 5px;
+      color: $ios-text-primary;
+      font-weight: 500;
+    }
+    input[type="number"],
+    select {
+      width: 100%;
+      padding: 10px;
+      border: 1px solid $ios-border;
+      border-radius: $ios-radius-lg;
+      font-size: 14px;
+      background-color: white;
+      &:focus {
+        outline: none;
+        border-color: $ios-primary;
+        box-shadow: 0 0 0 2px rgba(0, 71, 171, 0.1);
+      }
+    }
+  }
+  
+  .btn-calculate {
+    background-color: $ios-primary;
+    color: white;
+    border: none;
+    border-radius: $ios-radius-lg;
+    padding: 12px 20px;
+    font-size: 16px;
+    font-weight: 500;
+    cursor: pointer;
+    width: 100%;
+    transition: background-color 0.3s ease;
+    &:hover:not(:disabled) {
+      background-color: $ios-primary-light;
+    }
+    &:disabled {
+      background-color: $ios-text-tertiary;
+      cursor: not-allowed;
+    }
+  }
+}
+
+.workload-estimation-result {
+  margin-top: 20px;
+  padding: 15px;
+  background-color: $ios-background-secondary;
+  border-radius: $ios-radius-lg;
+  
+  .result-header {
+    margin-bottom: 15px;
+    h3 {
+      color: $ios-text-primary;
+      font-size: 16px;
+    }
+  }
+  
+  .result-content {
+    .total-workload {
+      text-align: center;
+      margin-bottom: 20px;
+      .total-label {
+        color: $ios-text-secondary;
+        font-size: 14px;
+        margin-bottom: 5px;
+      }
+      .total-value {
+        color: $ios-primary;
+        font-size: 32px;
+        font-weight: 700;
+        margin: 0;
+      }
+    }
+    
+    .workload-breakdown {
+      margin-bottom: 20px;
+      h4 {
+        color: $ios-text-primary;
+        font-size: 14px;
+        margin-bottom: 10px;
+        font-weight: 600;
+      }
+      .breakdown-items {
+        .breakdown-item {
+          display: flex;
+          justify-content: space-between;
+          align-items: center;
+          padding: 10px;
+          background-color: white;
+          border-radius: $ios-radius-lg;
+          margin-bottom: 8px;
+          .item-label {
+            color: $ios-text-primary;
+            font-size: 14px;
+          }
+          .item-value {
+            color: $ios-text-secondary;
+            font-size: 14px;
+            font-weight: 600;
+          }
+        }
+      }
+    }
+    
+    .workload-suggestions {
+      h4 {
+        color: $ios-text-primary;
+        font-size: 14px;
+        margin-bottom: 10px;
+        font-weight: 600;
+      }
+      ul {
+        list-style: none;
+        padding: 0;
+        li {
+          color: $ios-text-secondary;
+          font-size: 13px;
+          margin-bottom: 5px;
+          padding-left: 15px;
+          position: relative;
+          &::before {
+            content: "•";
+            color: $ios-primary;
+            position: absolute;
+            left: 0;
+            font-weight: bold;
+          }
+        }
+      }
+    }
+  }
+}
+
 /* 响应式设计 */
 @media (max-width: 768px) {
   .project-review-main {
@@ -562,7 +727,8 @@
   }
   
   .feasibility-section,
-  .assignment-section {
+  .assignment-section,
+  .workload-estimation-section {
     padding: 15px;
   }
   
@@ -570,4 +736,13 @@
   .designer-cards {
     grid-template-columns: 1fr !important;
   }
+  
+  .workload-estimation-section .section-header {
+    flex-direction: column;
+    align-items: flex-start;
+    .btn-toggle {
+      margin-top: 10px;
+      align-self: flex-end;
+    }
+  }
 }

+ 152 - 0
src/app/pages/team-leader/project-review/project-review.ts

@@ -63,6 +63,23 @@ interface AssignmentResult {
   assignTime: Date;
 }
 
+// 工作量预估相关接口
+interface WorkloadEstimationForm {
+  area: number;
+  complexity: 'low' | 'medium' | 'high';
+  deliveryRequirements: 'standard' | 'premium' | 'vip';
+}
+
+interface WorkloadEstimationResult {
+  totalHours: number;
+  breakdown: {
+    modeling: number;
+    rendering: number;
+    postProduction: number;
+  };
+  suggestions: string[];
+}
+
 @Component({
   selector: 'app-project-review',
   templateUrl: './project-review.html',
@@ -82,11 +99,20 @@ export class ProjectReviewComponent implements OnInit {
     specifiedDesignerId: ''
   };
   
+  // 工作量预估表单
+  workloadEstimationForm: WorkloadEstimationForm = {
+    area: 0,
+    complexity: 'medium',
+    deliveryRequirements: 'standard'
+  };
+  
   // 状态数据
   selectedImage: string | null = null;
   testResult: TestResult | null = null;
   designerWorkloadStatus: DesignerWorkloadStatus | null = null;
   assignmentResult: AssignmentResult | null = null;
+  workloadEstimationResult: WorkloadEstimationResult | null = null;
+  showWorkloadEstimator: boolean = false;
   
   // 模拟数据
   availableProjects: Project[] = [];
@@ -377,4 +403,130 @@ export class ProjectReviewComponent implements OnInit {
       this.getRecommendedDesigners();
     }
   }
+  
+  // 计算工作量预估
+  calculateWorkloadEstimation(): void {
+    // 重置之前的结果
+    this.workloadEstimationResult = null;
+    
+    // 验证表单数据
+    if (!this.workloadEstimationForm.area || this.workloadEstimationForm.area <= 0) {
+      alert('请输入有效的面积');
+      return;
+    }
+    
+    // 模拟计算过程
+    // 在实际应用中,这里应该根据具体的业务规则和算法进行计算
+    
+    // 根据面积、复杂度和交付要求计算总工时
+    const areaMultiplier = this.workloadEstimationForm.area / 100;
+    const complexityMultiplier = this.getComplexityMultiplier();
+    const deliveryMultiplier = this.getDeliveryMultiplier();
+    
+    // 基础工时计算
+    const baseHours = 20; // 基础工时
+    const totalHours = Math.round(baseHours * areaMultiplier * complexityMultiplier * deliveryMultiplier);
+    
+    // 各阶段工时分配
+    const modelingHours = Math.round(totalHours * 0.4);
+    const renderingHours = Math.round(totalHours * 0.35);
+    const postProductionHours = Math.round(totalHours * 0.25);
+    
+    // 生成建议
+    const suggestions = this.generateWorkloadSuggestions(totalHours, modelingHours);
+    
+    // 存储计算结果
+    this.workloadEstimationResult = {
+      totalHours,
+      breakdown: {
+        modeling: modelingHours,
+        rendering: renderingHours,
+        postProduction: postProductionHours
+      },
+      suggestions
+    };
+  }
+  
+  // 获取复杂度乘数
+  private getComplexityMultiplier(): number {
+    switch (this.workloadEstimationForm.complexity) {
+      case 'low':
+        return 0.8;
+      case 'medium':
+        return 1.0;
+      case 'high':
+        return 1.5;
+      default:
+        return 1.0;
+    }
+  }
+  
+  // 获取交付要求乘数
+  private getDeliveryMultiplier(): number {
+    switch (this.workloadEstimationForm.deliveryRequirements) {
+      case 'standard':
+        return 1.0;
+      case 'premium':
+        return 1.2;
+      case 'vip':
+        return 1.5;
+      default:
+        return 1.0;
+    }
+  }
+  
+  // 生成工作量建议
+  private generateWorkloadSuggestions(totalHours: number, modelingHours: number): string[] {
+    const suggestions = [];
+    
+    if (totalHours > 100) {
+      suggestions.push('项目工时较长,建议安排资深设计师或增加设计人员');
+    }
+    
+    if (modelingHours > 50) {
+      suggestions.push('建模阶段工时较多,建议在建模前进行详细的需求确认');
+    }
+    
+    if (this.workloadEstimationForm.complexity === 'high' && this.workloadEstimationForm.deliveryRequirements === 'vip') {
+      suggestions.push('复杂度高且交付要求高,建议增加项目检查点和评审次数');
+    }
+    
+    if (suggestions.length === 0) {
+      suggestions.push('项目工作量估算合理,可以按照正常流程进行');
+    }
+    
+    return suggestions;
+  }
+  
+  // 格式化工时显示
+  formatWorkloadHours(hours: number): string {
+    if (hours < 8) {
+      return `${hours}小时`;
+    }
+    const days = Math.floor(hours / 8);
+    const remainingHours = hours % 8;
+    if (remainingHours === 0) {
+      return `${days}天`;
+    }
+    return `${days}天${remainingHours}小时`;
+  }
+  
+  // 重置工作量预估表单
+  resetWorkloadEstimationForm(): void {
+    this.workloadEstimationForm = {
+      area: 0,
+      complexity: 'medium',
+      deliveryRequirements: 'standard'
+    };
+    this.workloadEstimationResult = null;
+  }
+  
+  // 切换工作量预估工具显示状态
+  toggleWorkloadEstimator(): void {
+    this.showWorkloadEstimator = !this.showWorkloadEstimator;
+    if (this.showWorkloadEstimator) {
+      // 如果打开,重置表单
+      this.resetWorkloadEstimationForm();
+    }
+  }
 }

+ 11 - 5
src/app/pages/team-leader/quality-management/quality-management.html

@@ -12,10 +12,16 @@
     
     <div class="sop-tabs">
       <div class="tab-buttons">
-        <button (click)="activeTab = 'modeling'" [class.active]="activeTab === 'modeling'">建模阶段</button>
-        <button (click)="activeTab = 'rendering'" [class.active]="activeTab === 'rendering'">渲染阶段</button>
-        <button (click)="activeTab = 'postProduction'" [class.active]="activeTab === 'postProduction'">后期阶段</button>
-      </div>
+          <button (click)="activeTab = 'modeling'" [class.active]="activeTab === 'modeling'">
+            <span>建模阶段</span>
+          </button>
+          <button (click)="activeTab = 'rendering'" [class.active]="activeTab === 'rendering'">
+            <span>渲染阶段</span>
+          </button>
+          <button (click)="activeTab = 'postProduction'" [class.active]="activeTab === 'postProduction'">
+            <span>后期阶段</span>
+          </button>
+        </div>
       
       <div class="tab-content">
         <!-- 建模阶段标准 -->
@@ -200,7 +206,7 @@
     
     <div class="rectification-list">
       @for (task of filteredTasks; track task.id) {
-      <div class="rectification-item" [class.priority-high]="task.priority === 'high'">
+      <div class="rectification-item" [class.priority-high]="task.priority === 'high'" [class.priority-medium]="task.priority === 'medium'" [class.priority-low]="task.priority === 'low'">
         <div class="rectification-header">
           <h4>{{ task.projectName }}</h4>
           <span [class]="'rectification-status status-' + task.status">{{ getStatusText(task.status) }}</span>

+ 242 - 34
src/app/pages/team-leader/quality-management/quality-management.scss

@@ -838,72 +838,116 @@
   }
 }
 
-// SOP标签页 - iOS风格美化
+// SOP标签页 - 重写为白色底色蓝色切换效果
 .sop-tabs {
   margin-bottom: $ios-spacing-lg;
   
   .tab-buttons {
     display: flex;
-    border-bottom: 1px solid $ios-border;
+    background-color: #ffffff; // 设置底色为白色
+    border-radius: 12px; // 圆角效果
+    padding: 4px; // 内边距
+    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05); // 轻微阴影
+    border: 1px solid #f0f0f0; // 细微边框
     margin-bottom: $ios-spacing-lg;
-    padding-bottom: $ios-spacing-sm;
     
-    .tab-button {
-      // iOS风格标签按钮
-      padding: $ios-spacing-sm $ios-spacing-xl;
+    button {
+      flex: 1; // 按钮平均分配宽度
+      padding: 12px 16px; // 内边距
       background-color: transparent;
       border: none;
-      border-bottom: 3px solid transparent;
-      color: $ios-text-secondary;
-      font-size: $ios-font-size-base;
+      color: #666666; // 默认文字颜色
+      font-size: 15px; // 字体大小
+      font-weight: 500; // 字重
       cursor: pointer;
       font-family: $ios-font-family;
       transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
       position: relative;
-      overflow: hidden;
+      text-align: center;
+      border-radius: 8px; // 按钮圆角
+      
+      // 文字容器 - 确保文字在最上层
+      span {
+        position: relative;
+        z-index: 10; // 文字层级最高
+        display: inline-block;
+      }
+      
+      // 左侧矩形色块修饰
+      &::before {
+        content: '';
+        position: absolute;
+        top: 0;
+        left: 0;
+        width: 4px;
+        height: 100%;
+        background-color: transparent;
+        transition: background-color 0.3s ease;
+        z-index: 5;
+      }
       
       &.active {
-        // iOS风格活动标签效果
-        color: $ios-primary;
-        border-bottom-color: $ios-primary;
-        font-weight: $ios-font-weight-semibold;
-        // 添加iOS风格的背景渐变
-        background: linear-gradient(90deg, rgba(0, 71, 171, 0.1) 0%, transparent 100%);
+        color: #ffffff; // 活动状态文字为白色
+        font-weight: 600; // 活动状态字重
+        
+        // 左侧蓝色色块
+        &::before {
+          background-color: #007aff; // 左侧蓝色色块
+        }
+        
+        // 蓝色背景填充 - 直接设置背景色,不使用伪元素和动画
+        background-color: #007aff;
       }
       
       &:hover:not(.active) {
-        color: $ios-primary;
-        background-color: $ios-background-secondary;
+        background-color: rgba(0, 122, 255, 0.05); // 轻微蓝色背景
+        color: #007aff; // 文字变为蓝色
       }
       
-      // 添加iOS风格的标签点击效果
+      // 点击效果
       &:active {
-        opacity: 0.7;
+        transform: scale(0.98);
       }
     }
   }
 }
 
-// SOP内容样式
+// 标签切换动画
+@keyframes tabSlideIn {
+  0% {
+    transform: translateX(100%);
+    opacity: 0;
+  }
+  100% {
+    transform: translateX(0);
+    opacity: 1;
+  }
+}
+
+// SOP内容样式 - 修改为三栏布局
 .phase-content {
-  background-color: $ios-background;
-  border-radius: $ios-radius-xl;
-  padding: $ios-spacing-xl;
-  box-shadow: $ios-shadow-md;
-  border: 1px solid $ios-border;
+  display: flex; // 使用flex布局
+  gap: $ios-spacing-lg; // 卡片间距
+  background-color: transparent; // 移除背景色
+  padding: 0; // 移除内边距
+  box-shadow: none; // 移除阴影
+  border: none; // 移除边框
   transform: translateY(0);
   transition: all 0.3s ease;
   
-  &:hover {
-    box-shadow: $ios-shadow-lg;
-    transform: translateY(-2px);
-  }
-  
   .sop-section {
-    margin-bottom: $ios-spacing-xxl;
+    flex: 1; // 每个卡片占三分之一宽度
+    background-color: #ffffff; // 白色背景
+    border-radius: 12px; // 圆角效果
+    padding: $ios-spacing-lg; // 内边距
+    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05); // 轻微阴影
+    border: 1px solid #f0f0f0; // 细微边框
+    margin-bottom: 0; // 移除底部间距
     
-    &:last-child {
-      margin-bottom: 0;
+    &:hover {
+      box-shadow: 0 6px 16px rgba(0, 0, 0, 0.08); // 悬停时加深阴影
+      transform: translateY(-2px); // 轻微上浮效果
+      transition: all 0.3s ease;
     }
     
     h3 {
@@ -1531,6 +1575,170 @@
   }
 }
 
+// 质量整改跟踪板块 - 搜索和筛选样式
+.rectification-section {
+  .section-header {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    margin-bottom: $ios-spacing-xl;
+  }
+  
+  .search-filter {
+    display: flex;
+    align-items: center;
+    gap: $ios-spacing-md;
+    padding: $ios-spacing-sm $ios-spacing-md;
+    background-color: $ios-background;
+    border: 1px solid $ios-border;
+    border-radius: $ios-radius-lg;
+    box-shadow: $ios-shadow-sm;
+  }
+  
+  .search-filter input {
+    flex: 1;
+    padding: $ios-spacing-sm $ios-spacing-md;
+    border: 1px solid $ios-border;
+    border-radius: $ios-radius-md;
+    font-size: $ios-font-size-sm;
+    color: $ios-text-primary;
+    background-color: $ios-background-secondary;
+    font-family: $ios-font-family;
+    transition: all 0.2s ease;
+    min-width: 200px;
+  }
+  
+  .search-filter input:focus {
+    outline: none;
+    border-color: $ios-primary;
+    background-color: white;
+    box-shadow: 0 0 0 2px rgba(0, 71, 171, 0.1);
+  }
+  
+  .search-filter select {
+    padding: $ios-spacing-sm $ios-spacing-md;
+    border: 1px solid $ios-border;
+    border-radius: $ios-radius-md;
+    font-size: $ios-font-size-sm;
+    color: $ios-text-primary;
+    background-color: $ios-background-secondary;
+    font-family: $ios-font-family;
+    cursor: pointer;
+    transition: all 0.2s ease;
+    min-width: 120px;
+    -webkit-appearance: none;
+    -moz-appearance: none;
+    appearance: none;
+    background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="%238E8E93" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"></polyline></svg>');
+    background-repeat: no-repeat;
+    background-position: right $ios-spacing-md center;
+    background-size: 16px;
+    padding-right: $ios-spacing-lg;
+  }
+  
+  .search-filter select:focus {
+    outline: none;
+    border-color: $ios-primary;
+    background-color: white;
+    box-shadow: 0 0 0 2px rgba(0, 71, 171, 0.1);
+  }
+  
+  .search-filter select::-ms-expand {
+    display: none;
+  }
+  
+  // 整改任务列表样式
+  .rectification-list {
+    display: grid;
+    grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
+    gap: $ios-spacing-xl;
+  }
+  
+  // 整改任务卡片样式 - 现代感边缘优化
+  .rectification-item {
+    padding: $ios-spacing-lg;
+    border-radius: $ios-radius-xl;
+    background-color: $ios-background;
+    border: 1px solid $ios-border;
+    box-shadow: $ios-shadow-md;
+    transition: all 0.3s ease;
+    overflow: hidden;
+    position: relative;
+    // 添加现代感边缘光晕效果
+    box-shadow: 
+      0 4px 6px rgba(0, 0, 0, 0.05),
+      0 1px 3px rgba(0, 0, 0, 0.1),
+      0 0 0 1px rgba(0, 71, 171, 0.03);
+    
+    // 悬停效果增强
+    &:hover {
+      box-shadow: 
+        0 10px 15px -3px rgba(0, 0, 0, 0.08),
+        0 4px 6px -2px rgba(0, 0, 0, 0.04),
+        0 0 0 2px rgba(0, 71, 171, 0.07);
+      transform: translateY(-2px);
+    }
+    
+    // 高优先级任务特殊边缘样式
+    &.priority-high {
+      border-left: 4px solid $ios-danger;
+      // 添加红色边缘发光效果
+      box-shadow: 
+        0 4px 6px rgba(0, 0, 0, 0.05),
+        0 1px 3px rgba(0, 0, 0, 0.1),
+        0 0 0 1px rgba(255, 59, 48, 0.05),
+        inset 0 0 0 1px rgba(255, 59, 48, 0.1);
+    }
+    
+    &.priority-medium {
+      border-left: 4px solid $ios-warning;
+      // 添加黄色边缘发光效果
+      box-shadow: 
+        0 4px 6px rgba(0, 0, 0, 0.05),
+        0 1px 3px rgba(0, 0, 0, 0.1),
+        0 0 0 1px rgba(255, 149, 0, 0.05),
+        inset 0 0 0 1px rgba(255, 149, 0, 0.1);
+    }
+    
+    &.priority-low {
+      border-left: 4px solid $ios-success;
+      // 添加绿色边缘发光效果
+      box-shadow: 
+        0 4px 6px rgba(0, 0, 0, 0.05),
+        0 1px 3px rgba(0, 0, 0, 0.1),
+        0 0 0 1px rgba(52, 199, 89, 0.05),
+        inset 0 0 0 1px rgba(52, 199, 89, 0.1);
+    }
+  }
+  
+  // 卡片头部样式
+  .rectification-header {
+    display: flex;
+    justify-content: space-between;
+    align-items: flex-start;
+    margin-bottom: $ios-spacing-md;
+    
+    h4 {
+      font-size: $ios-font-size-base;
+      font-weight: $ios-font-weight-semibold;
+      color: $ios-text-primary;
+      margin: 0;
+      line-height: 1.4;
+    }
+  }
+  
+  // 状态标签样式
+  .rectification-status {
+    font-size: $ios-font-size-xs;
+    padding: 4px $ios-spacing-sm;
+    border-radius: $ios-radius-full;
+    font-weight: $ios-font-weight-medium;
+    white-space: nowrap;
+    text-transform: uppercase;
+    letter-spacing: 0.5px;
+  }
+}
+
 // iOS风格滚动条
 ::-webkit-scrollbar {
   width: $ios-scrollbar-width;