Browse Source

feat:designer-new001

0235711 9 hours ago
parent
commit
28a6a217f9

+ 15 - 0
.vscode/settings.json

@@ -0,0 +1,15 @@
+{
+  "json.schemas": [
+    {
+      "fileMatch": ["tsconfig.json", "tsconfig.app.json", "tsconfig.*.json"],
+      "url": "https://json.schemastore.org/tsconfig",
+      "enable": false
+    }
+  ],
+  "json.validate": false,
+  "files.associations": {
+    "tsconfig.json": "jsonc",
+    "tsconfig.app.json": "jsonc",
+    "*.json": "jsonc"
+  }
+}

+ 3 - 0
angular.json

@@ -23,6 +23,9 @@
             ],
             "tsConfig": "tsconfig.app.json",
             "inlineStyleLanguage": "scss",
+            "externalDependencies": [
+              "echarts"
+            ],
             "assets": [
               {
                 "glob": "**/*",

+ 121 - 126
src/app/app.scss

@@ -25,6 +25,7 @@ $transition: all 0.3s ease;
   box-sizing: border-box;
 }
 
+
 body {
   font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
   color: $text-primary;
@@ -40,139 +41,118 @@ body {
 }
 
 // 顶部导航栏
-.top-navbar {
+top-navbar {
   display: flex;
   align-items: center;
   justify-content: space-between;
   padding: 0 24px;
   height: 64px;
   background-color: $background-primary;
-  border-bottom: 1px solid $border-color;
   box-shadow: $shadow-sm;
-  position: sticky;
-  top: 0;
   z-index: 1000;
+}
 
-  .navbar-left {
-    display: flex;
-    align-items: center;
-    gap: 16px;
-  }
-
-  .menu-toggle {
-    display: none;
-    background: none;
-    border: none;
-    cursor: pointer;
-    color: $text-primary;
-    padding: 8px;
-    transition: $transition;
-
-    &:hover {
-      color: $primary-color;
-    }
-  }
+.navbar-left {
+  display: flex;
+  align-items: center;
+  gap: 16px;
+}
 
-  .app-title {
-    font-size: 20px;
-    font-weight: 600;
-    color: $text-primary;
-  }
+.app-title {
+  font-size: 20px;
+  font-weight: 600;
+  color: $primary-color;
+  margin: 0;
+}
 
-  .navbar-center {
-    flex: 1;
-    max-width: 500px;
-    margin: 0 20px;
-  }
+.navbar-center {
+  display: flex;
+  flex: 1;
+  max-width: 400px;
+  margin: 0 24px;
+}
 
-  .search-container {
-    display: flex;
-    align-items: center;
-    background-color: $background-tertiary;
-    border-radius: $border-radius;
-    padding: 6px 12px;
-    border: 1px solid $border-color;
+.search-container {
+  position: relative;
+  width: 100%;
+}
 
-    .search-input {
-      flex: 1;
-      background: none;
-      border: none;
-      outline: none;
-      padding: 6px 8px;
-      font-size: 14px;
-      color: $text-primary;
+.search-input {
+  width: 100%;
+  padding: 8px 12px 8px 36px;
+  border: 1px solid $border-color;
+  border-radius: $border-radius;
+  font-size: 14px;
+  transition: $transition;
+}
 
-      &::placeholder {
-        color: $text-tertiary;
-      }
-    }
+.search-input:focus {
+  outline: none;
+  border-color: $primary-color;
+  box-shadow: 0 0 0 2px color-mix(in srgb, $primary-color 20%, transparent);
+}
 
-    .search-button {
-      background: none;
-      border: none;
-      cursor: pointer;
-      color: $text-secondary;
-      padding: 4px;
-      transition: $transition;
+.search-icon {
+  position: absolute;
+  left: 12px;
+  top: 50%;
+  transform: translateY(-50%);
+  color: $text-tertiary;
+}
 
-      &:hover {
-        color: $primary-color;
-      }
-    }
-  }
+.navbar-right {
+  display: flex;
+  align-items: center;
+  gap: 16px;
+}
 
-  .navbar-right {
-    display: flex;
-    align-items: center;
-    gap: 16px;
-  }
+.notification-btn {
+  position: relative;
+  background: none;
+  border: none;
+  cursor: pointer;
+  color: $text-secondary;
+  transition: $transition;
+}
 
-  .notification-btn {
-    position: relative;
-    background: none;
-    border: none;
-    cursor: pointer;
-    color: $text-secondary;
-    padding: 8px;
-    transition: $transition;
-
-    &:hover {
-      color: $primary-color;
-    }
+.notification-btn:hover {
+  color: $primary-color;
+}
 
-    .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;
-    }
-  }
+.notification-badge {
+  position: absolute;
+  top: -4px;
+  right: -4px;
+  background-color: $danger-color;
+  color: white;
+  font-size: 10px;
+  padding: 2px 4px;
+  border-radius: 8px;
+  min-width: 16px;
+  text-align: center;
+}
 
-  .user-profile {
-    display: flex;
-    align-items: center;
-    gap: 8px;
+.user-profile {
+  display: flex;
+  align-items: center;
+  gap: 8px;
+}
 
-    .user-avatar {
-      width: 36px;
-      height: 36px;
-      border-radius: 50%;
-      object-fit: cover;
-    }
+.user-avatar {
+  width: 36px;
+  height: 36px;
+  border-radius: 50%;
+  background-color: $primary-color;
+  color: white;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  font-weight: 500;
+}
 
-    .user-name {
-      font-size: 14px;
-      font-weight: 500;
-      color: $text-primary;
-    }
-  }
+.user-name {
+  font-size: 14px;
+  color: $text-secondary;
 }
 
 // 主要内容区
@@ -256,16 +236,31 @@ body {
   }
 }
 
+// 设计师页面特定规则 - 彻底隐藏侧边栏
+.designer-page .sidebar {
+  display: none !important;
+  width: 0 !important;
+  visibility: hidden !important;
+  position: absolute !important;
+  left: -9999px !important;
+}
+
+.designer-page .content-wrapper {
+  width: 100% !important;
+  margin-left: 0 !important;
+  padding: 20px !important;
+}
+
+.designer-page .main-content {
+  padding: 0 !important;
+  margin: 0 !important;
+}
+
 // 中间内容区
 .content-wrapper {
   flex: 1;
+  padding: 20px;
   overflow-y: auto;
-  padding: 24px;
-  transition: $transition;
-
-  &.expanded {
-    width: 100%;
-  }
 }
 
 // 响应式设计
@@ -278,18 +273,18 @@ body {
 @media (max-width: 768px) {
   .top-navbar {
     padding: 0 16px;
+  }
 
-    .menu-toggle {
-      display: block;
-    }
+  .menu-toggle {
+    display: block;
+  }
 
-    .app-title {
-      font-size: 18px;
-    }
+  .app-title {
+    font-size: 18px;
+  }
 
-    .navbar-center {
-      display: none;
-    }
+  .navbar-center {
+    display: none;
   }
 
   .sidebar {

+ 50 - 43
src/app/pages/admin/dashboard/dashboard.ts

@@ -1,9 +1,12 @@
-import { Component, OnInit, AfterViewInit, signal, OnDestroy } from '@angular/core';
 import { CommonModule } from '@angular/common';
 import { RouterModule } from '@angular/router';
-import * as echarts from 'echarts';
 import { Subscription } from 'rxjs';
+import { signal } from '@angular/core';
 import { AdminDashboardService } from './dashboard.service';
+// 使用全局echarts变量,不导入模块
+
+// 确保@Component装饰器存在
+import { Component, OnInit, AfterViewInit, OnDestroy } from '@angular/core';
 
 @Component({
   selector: 'app-admin-dashboard',
@@ -24,8 +27,8 @@ export class AdminDashboard implements OnInit, AfterViewInit, OnDestroy {
   };
 
   private subscriptions: Subscription = new Subscription();
-  private projectChart: echarts.ECharts | null = null;
-  private revenueChart: echarts.ECharts | null = null;
+  private projectChart: any | null = null;
+  private revenueChart: any | null = null;
 
   constructor(private dashboardService: AdminDashboardService) {}
 
@@ -66,52 +69,56 @@ export class AdminDashboard implements OnInit, AfterViewInit, OnDestroy {
     const projectChartDom = document.getElementById('projectTrendChart');
     if (projectChartDom) {
       this.projectChart = echarts.init(projectChartDom);
-      this.projectChart.setOption({
-        title: { text: '项目数量趋势', left: 'center', textStyle: { fontSize: 16 } },
-        tooltip: { trigger: 'axis' },
-        xAxis: { type: 'category', data: ['1月', '2月', '3月', '4月', '5月', '6月'] },
-        yAxis: { type: 'value' },
-        series: [{
-          name: '新项目',
-          type: 'line',
-          data: [18, 25, 32, 28, 42, 38],
-          lineStyle: { color: '#165DFF' },
-          itemStyle: { color: '#165DFF' }
-        }, {
-          name: '完成项目',
-          type: 'line',
-          data: [15, 20, 25, 22, 35, 30],
-          lineStyle: { color: '#00B42A' },
-          itemStyle: { color: '#00B42A' }
-        }]
-      });
+      if (this.projectChart) {
+        this.projectChart.setOption({
+          title: { text: '项目数量趋势', left: 'center', textStyle: { fontSize: 16 } },
+          tooltip: { trigger: 'axis' },
+          xAxis: { type: 'category', data: ['1月', '2月', '3月', '4月', '5月', '6月'] },
+          yAxis: { type: 'value' },
+          series: [{
+            name: '新项目',
+            type: 'line',
+            data: [18, 25, 32, 28, 42, 38],
+            lineStyle: { color: '#165DFF' },
+            itemStyle: { color: '#165DFF' }
+          }, {
+            name: '完成项目',
+            type: 'line',
+            data: [15, 20, 25, 22, 35, 30],
+            lineStyle: { color: '#00B42A' },
+            itemStyle: { color: '#00B42A' }
+          }]
+        });
+      }
     }
 
     // 初始化收入统计图
     const revenueChartDom = document.getElementById('revenueChart');
     if (revenueChartDom) {
       this.revenueChart = echarts.init(revenueChartDom);
-      this.revenueChart.setOption({
-        title: { text: '季度收入统计', left: 'center', textStyle: { fontSize: 16 } },
-        tooltip: { trigger: 'item' },
-        series: [{
-          name: '收入分布',
-          type: 'pie',
-          radius: '65%',
-          data: [
-            { value: 350000, name: '第一季度' },
-            { value: 420000, name: '第二季度' },
-            { value: 488000, name: '第三季度' }
-          ],
-          emphasis: {
-            itemStyle: {
-              shadowBlur: 10,
-              shadowOffsetX: 0,
-              shadowColor: 'rgba(0, 0, 0, 0.5)'
+      if (this.revenueChart) {
+        this.revenueChart.setOption({
+          title: { text: '季度收入统计', left: 'center', textStyle: { fontSize: 16 } },
+          tooltip: { trigger: 'item' },
+          series: [{
+            name: '收入分布',
+            type: 'pie',
+            radius: '65%',
+            data: [
+              { value: 350000, name: '第一季度' },
+              { value: 420000, name: '第二季度' },
+              { value: 488000, name: '第三季度' }
+            ],
+            emphasis: {
+              itemStyle: {
+                shadowBlur: 10,
+                shadowOffsetX: 0,
+                shadowColor: 'rgba(0, 0, 0, 0.5)'
+              }
             }
-          }
-        }]
-      });
+          }]
+        });
+      }
     }
   }
 

+ 2 - 1
src/app/pages/admin/system-settings/system-settings.scss

@@ -1,4 +1,5 @@
-// 全局变量定义
+// 注意:以下变量应该从共享样式文件导入,但由于导入问题暂时在此重新定义
+// 未来优化目标:修复导入问题,使用共享的_variables.scss文件
 $primary-color: #165DFF;
 $secondary-color: #6B7280;
 $success-color: #00B42A;

+ 116 - 155
src/app/pages/designer/dashboard/dashboard.html

@@ -1,54 +1,50 @@
 <div class="dashboard-container">
   <header class="dashboard-header">
     <h1>设计师工作台</h1>
+    
+    <!-- 顶部导航 -->
+    <nav class="dashboard-nav">
+      <button class="nav-btn active" (click)="switchDashboard('main')">工作台</button>
+      <button class="nav-btn" (click)="switchDashboard('skills')">能力雷达</button>
+      <button class="nav-btn" (click)="switchDashboard('personal')">个人看板</button>
+    </nav>
   </header>
 
-  <main class="dashboard-main">
-    <!-- 紧急任务区域 -->
-    <section class="urgent-section" *ngIf="urgentTasks.length > 0">
-      <div class="section-header">
-        <h2>紧急任务</h2>
-      </div>
-      
-      <div class="urgent-list">
-        <div *ngFor="let task of urgentTasks" class="urgent-item">
-          <div class="urgent-content">
-            <p class="urgent-title">⚠️ {{ task.title }} - 渲染即将超期</p>
-            <p class="urgent-detail">项目: {{ task.projectName }},剩余时间: {{ getTaskCountdown(task.id) }}</p>
+  <!-- 主要内容区域 - 工作台 -->
+  <div *ngIf="activeDashboard === 'main'" class="dashboard-main">
+    <!-- 核心信息卡片区域 - 每行列3张卡片 -->
+    <section class="core-cards-section">
+      <div class="cards-grid">
+        <!-- 紧急任务卡片 -->
+        <div *ngFor="let task of urgentTasks" class="core-card urgent-card">
+          <div class="card-header">
+            <span class="card-badge urgent">紧急</span>
+            <h3>{{ task.title }}</h3>
+          </div>
+          <div class="card-content">
+            <p class="project-name">项目: {{ task.projectName }}</p>
+            <p class="countdown">剩余: {{ getTaskCountdown(task.id) }}</p>
           </div>
-          <div class="urgent-actions">
-            <button [routerLink]="['/designer/project-detail', task.projectId]" class="btn-urgent-detail">
+          <div class="card-actions">
+            <button [routerLink]="['/designer/project-detail', task.projectId]" class="btn-primary">
               立即处理
             </button>
           </div>
         </div>
-      </div>
-    </section>
-
-    <!-- 待办任务区域 -->
-    <section class="task-section">
-      <div class="section-header">
-        <h2>待办任务</h2>
-      </div>
-      
-      <div class="task-list">
-        <div *ngIf="tasks.length === 0" class="empty-state">
-          暂无待办任务
-        </div>
         
-        <div *ngFor="let task of tasks" class="task-item">
-          <div class="task-header">
+        <!-- 待办任务卡片 -->
+        <div *ngFor="let task of getTopTasks(6 - urgentTasks.length)" class="core-card task-card">
+          <div class="card-header">
+            <span class="card-badge" [class.overdue]="task.isOverdue">
+              {{ task.stage }}
+              <span *ngIf="task.isOverdue">/超期</span>
+            </span>
             <h3>{{ task.title }}</h3>
-            <span class="task-stage">{{ task.stage }}</span>
           </div>
-          <div class="task-info">
+          <div class="card-content">
             <p class="project-name">项目: {{ task.projectName }}</p>
             <p class="deadline" [class.overdue]="task.isOverdue">
-              截止日期: {{ task.deadline | date:'yyyy-MM-dd HH:mm' }}
-              <span *ngIf="task.isOverdue" class="overdue-badge">已超期</span>
-              <span *ngIf="task.stage === '渲染' && !task.isOverdue" class="countdown-badge">
-                剩余: {{ getTaskCountdown(task.id) }}
-              </span>
+              截止: {{ task.deadline | date:'yyyy-MM-dd HH:mm' }}
             </p>
             
             <!-- 进度条 -->
@@ -60,157 +56,122 @@
               <p class="progress-text">{{ getTaskStageProgress(task.id) }}%</p>
             </div>
           </div>
-          <div class="task-actions">
-            <button *ngIf="!task.isCompleted" (click)="markTaskAsCompleted(task.id)" class="btn-complete">
+          <div class="card-actions">
+            <button *ngIf="!task.isCompleted" (click)="markTaskAsCompleted(task.id)" class="btn-secondary">
               标记完成
             </button>
-            <button [routerLink]="['/designer/project-detail', task.projectId]" class="btn-detail">
+            <button [routerLink]="['/designer/project-detail', task.projectId]" class="btn-primary">
               查看详情
             </button>
           </div>
         </div>
-      </div>
-    </section>
-
-    <!-- 待处理反馈区域 -->
-    <section class="feedback-section" *ngIf="pendingFeedbacks.length > 0">
-      <div class="section-header">
-        <h2>待处理反馈</h2>
-      </div>
-      
-      <div class="feedback-list">
-        <div *ngFor="let item of pendingFeedbacks" class="feedback-item">
-          <div class="feedback-content">
-            <p class="feedback-title">⚠️ {{ item.task.title }} - 有客户反馈需处理</p>
-            <p class="feedback-project">项目: {{ item.task.projectName }}</p>
-            <p class="feedback-summary">反馈类型: {{ !item.feedback.isSatisfied ? '不满意' : '满意' }}</p>
-            <p class="feedback-time">反馈时间: {{ item.feedback.createdAt | date:'yyyy-MM-dd HH:mm' }}</p>
+        
+        <!-- 项目饱和度卡片 -->
+        <div class="core-card workload-card" *ngIf="(urgentTasks.length + tasks.length) < 6">
+          <div class="card-header">
+            <span class="card-badge workload">饱和度</span>
+            <h3>当前工作量</h3>
+          </div>
+          <div class="card-content">
+            <div class="workload-indicator">
+              <div class="workload-circle" [style.background]="getWorkloadColor()">
+                <span class="workload-percentage">{{ workloadPercentage }}%</span>
+              </div>
+            </div>
+            <p class="workload-status">{{ getWorkloadStatus() }}</p>
           </div>
-          <div class="feedback-actions">
-            <button (click)="handleFeedback(item.task.id)" class="btn-handle-feedback">
-              处理反馈
+          <div class="card-actions">
+            <button class="btn-secondary" (click)="switchDashboard('personal')">
+              查看详情
             </button>
           </div>
         </div>
       </div>
     </section>
 
-    <!-- 时间预警区域 -->
-    <section class="warning-section" *ngIf="overdueTasks.length > 0">
-      <div class="section-header">
-        <h2>时间预警</h2>
-      </div>
-      
-      <div class="warning-list">
-        <div *ngIf="overdueTasks.length === 0" class="empty-state">
-          暂无超期风险任务
+    <!-- 附加信息区域 -->
+    <section class="additional-info-section">
+      <!-- 待处理反馈区域 -->
+      <div class="info-column" *ngIf="pendingFeedbacks.length > 0">
+        <div class="section-header">
+          <h2>待处理反馈</h2>
         </div>
         
-        <div *ngFor="let task of overdueTasks" class="warning-item">
-          <div class="warning-content">
-            <p class="warning-title">{{ task.title }} - 已超期</p>
-            <p class="warning-detail">项目: {{ task.projectName }},截止日期: {{ task.deadline | date:'yyyy-MM-dd HH:mm' }}</p>
-          </div>
-          <div class="warning-actions">
-            <button (click)="generateReminderMessage()" class="btn-generate-reminder">
-              生成提醒话术
-            </button>
+        <div class="feedback-list">
+          <div *ngFor="let item of pendingFeedbacks" class="feedback-item">
+            <div class="feedback-content">
+              <p class="feedback-title">⚠️ {{ item.task.title }} - 客户反馈</p>
+              <p class="feedback-project">项目: {{ item.task.projectName }}</p>
+              <p class="feedback-summary">反馈: {{ !item.feedback.isSatisfied ? '不满意' : '满意' }}</p>
+            </div>
+            <div class="feedback-actions">
+              <button (click)="handleFeedback(item.task.id)" class="btn-handle-feedback">
+                处理反馈
+              </button>
+            </div>
           </div>
         </div>
       </div>
-    </section>
 
-    <!-- 设计师代班信息表 -->
-    <section class="shift-info">
-      <div class="section-header">
-        <h2>👥 代班信息</h2>
-        <button class="add-shift-btn" (click)="openShiftModal()">
-          添加代班任务
-        </button>
-      </div>
-      <div class="shift-list">
-        <div class="shift-item" *ngFor="let shift of shiftTasks">
-          <div class="shift-header">
-            <div class="shift-project">{{ shift.projectName }}</div>
-            <div class="shift-priority" [class.priority-high]="shift.priority === '高'" [class.priority-medium]="shift.priority === '中'" [class.priority-low]="shift.priority === '低'">
-              {{ shift.priority }}级
+      <!-- 代班信息区域 -->
+      <div class="info-column" *ngIf="shiftTasks.length > 0">
+        <div class="section-header">
+          <h2>👥 代班信息</h2>
+          <button class="add-shift-btn" (click)="openShiftModal()">
+            添加代班任务
+          </button>
+        </div>
+        <div class="shift-list">
+          <div class="shift-item" *ngFor="let shift of shiftTasks">
+            <div class="shift-header">
+              <div class="shift-project">{{ shift.projectName }}</div>
+              <div class="shift-priority" [class.priority-high]="shift.priority === '高'" [class.priority-medium]="shift.priority === '中'" [class.priority-low]="shift.priority === '低'">
+                {{ shift.priority }}级
+              </div>
+            </div>
+            <div class="shift-details">
+              <div class="shift-task">{{ shift.taskDescription }}</div>
+              <div class="shift-time">代班时间: {{ shift.shiftDate }}</div>
             </div>
-          </div>
-          <div class="shift-details">
-            <div class="shift-task">{{ shift.taskDescription }}</div>
-            <div class="shift-time">代班时间: {{ shift.shiftDate }}</div>
-          </div>
-          <div class="shift-actions">
-            <button class="view-detail-btn" (click)="viewShiftDetail(shift.id)">查看详情</button>
-            <button class="mark-complete-btn" (click)="markShiftComplete(shift.id)">标记完成</button>
           </div>
         </div>
       </div>
-    </section>
 
-    <!-- 个人项目饱和度 -->
-    <section class="workload-section">
-      <div class="section-header">
-        <h2>📊 项目饱和度</h2>
-      </div>
-      <div class="workload-container">
-        <div class="workload-bar">
-          <div 
-            class="workload-fill" 
-            [style.width]="workloadPercentage + '%'"
-            [class.workload-normal]="workloadPercentage < 75"
-            [class.workload-warning]="workloadPercentage >= 75 && workloadPercentage < 90"
-            [class.workload-critical]="workloadPercentage >= 90"
-          ></div>
+      <!-- 时间预警区域 -->
+      <div class="info-column" *ngIf="overdueTasks.length > 0">
+        <div class="section-header">
+          <h2>⏰ 时间预警</h2>
         </div>
-        <div class="workload-info">
-          <span class="workload-percentage">{{ workloadPercentage }}%</span>
-          <span class="workload-status">{{ getWorkloadStatus() }}</span>
-        </div>
-        <div class="project-timeline">
-          <h4>项目排期表</h4>
-          <div class="timeline-items">
-            <div class="timeline-item" *ngFor="let project of projectTimeline">
-              <div class="timeline-project">{{ project.name }}</div>
-              <div class="timeline-deadline">截止: {{ project.deadline }}</div>
+        
+        <div class="warning-list">
+          <div *ngFor="let task of overdueTasks" class="warning-item">
+            <div class="warning-content">
+              <p class="warning-title">{{ task.title }} - 已超期</p>
+              <p class="warning-detail">项目: {{ task.projectName }}</p>
             </div>
           </div>
         </div>
       </div>
     </section>
 
-    <!-- 能力维度雷达图 -->
-    <section class="skill-radar-section">
-      <div class="section-header">
-        <h2>能力维度雷达图</h2>
-      </div>
-      <app-skill-radar></app-skill-radar>
-    </section>
+    <!-- 底部信息分层展示 -->
+    <div class="bottom-sections">
+      <!-- 能力雷达区域 -->
+      <section class="bottom-section">
+        <app-skill-radar></app-skill-radar>
+      </section>
+    </div>
+  </div>
 
-    <!-- 快速入口区域 -->
-    <section class="quick-access-section">
-      <div class="section-header">
-        <h2>快速入口</h2>
-      </div>
-      
-      <div class="quick-access-grid">
-        <a [routerLink]="['/designer/project-detail', tasks[0].projectId || '']" [ngClass]="{ 'quick-access-item': true, 'has-items': tasks.length > 0 }" *ngIf="tasks.length > 0">
-          <h3>待确认项目</h3>
-          <p>{{ tasks.length }}个项目待处理</p>
-        </a>
-        
-        <a [routerLink]="['/designer/project-detail', feedbackProjectId]" [ngClass]="{ 'quick-access-item': true, 'has-items': pendingFeedbacks.length > 0 }" *ngIf="pendingFeedbacks.length > 0">
-          <h3>待反馈项目</h3>
-          <p>{{ pendingFeedbacks.length }}个项目需反馈</p>
-        </a>
-        
-        <a [routerLink]="['/designer/personal-board']" class="quick-access-item">
-          <h3>个人看板</h3>
-          <p>查看绩效与结算</p>
-        </a>
-      </div>
-    </section>
-  </main>
+  <!-- 能力雷达单独视图 -->
+  <div *ngIf="activeDashboard === 'skills'" class="skills-view">
+    <app-skill-radar></app-skill-radar>
+  </div>
+
+  <!-- 个人看板单独视图 -->
+  <div *ngIf="activeDashboard === 'personal'" class="personal-view">
+    <app-personal-board></app-personal-board>
+  </div>
 
   <!-- 提醒话术弹窗 -->
   <div *ngIf="reminderMessage" class="reminder-modal">

+ 776 - 7
src/app/pages/designer/dashboard/dashboard.scss

@@ -18,20 +18,789 @@
     color: $ios-text-primary;
     font-weight: $ios-font-weight-semibold;
     font-family: $ios-font-family;
+    margin-bottom: 20px;
+  }
+}
+
+/* 顶部导航样式 */
+.dashboard-nav {
+  display: flex;
+  background-color: $ios-background-secondary;
+  border-radius: $ios-radius-lg;
+  padding: 4px;
+  margin-bottom: 20px;
+  
+  .nav-btn {
+    flex: 1;
+    padding: 12px 24px;
+    border: none;
+    border-radius: $ios-radius-md;
+    background-color: transparent;
+    color: $ios-text-secondary;
+    font-size: 15px;
+    font-weight: $ios-font-weight-medium;
+    cursor: pointer;
+    transition: all 0.3s ease;
+    font-family: $ios-font-family;
+    
+    &.active {
+      background-color: $ios-card-background;
+      color: $ios-primary;
+      box-shadow: $ios-shadow-sm;
+    }
+    
+    &:hover:not(.active) {
+      background-color: rgba(0, 0, 0, 0.05);
+    }
   }
 }
 
 .dashboard-main {
   display: grid;
-  grid-template-columns: 1fr 1fr;
+  grid-template-columns: 1fr;
+  gap: 24px;
+}
+
+/* 核心卡片区域样式 */
+.core-cards-section {
+  margin-bottom: 30px;
+  
+  .cards-grid {
+    display: grid;
+    grid-template-columns: repeat(3, 1fr);
+    gap: 20px;
+    
+    @media (max-width: 1024px) {
+      grid-template-columns: repeat(2, 1fr);
+    }
+    
+    @media (max-width: 768px) {
+      grid-template-columns: 1fr;
+    }
+  }
+}
+
+/* 核心卡片基础样式 */
+.core-card {
+  background: $ios-card-background;
+  border-radius: $ios-radius-lg;
+  padding: 20px;
+  box-shadow: $ios-shadow-card;
+  transition: all 0.3s ease;
+  border: 1px solid $ios-border;
+  display: flex;
+  flex-direction: column;
+  height: 100%;
+  
+  &:hover {
+    box-shadow: $ios-shadow-lg;
+    transform: translateY(-2px);
+  }
+  
+  .card-header {
+    display: flex;
+    justify-content: space-between;
+    align-items: flex-start;
+    margin-bottom: 16px;
+    
+    .card-badge {
+      padding: 4px 12px;
+      border-radius: $ios-radius-full;
+      font-size: 12px;
+      font-weight: $ios-font-weight-medium;
+      color: $ios-text-secondary;
+      background-color: $ios-background-secondary;
+      
+      &.urgent {
+        background-color: rgba(255, 59, 48, 0.1);
+        color: $ios-danger;
+        border: 1px solid $ios-danger;
+      }
+      
+      &.workload {
+        background-color: rgba(52, 199, 89, 0.1);
+        color: $ios-success;
+        border: 1px solid $ios-success;
+      }
+      
+      &.overdue {
+        background-color: rgba(255, 149, 0, 0.1);
+        color: $ios-warning;
+        border: 1px solid $ios-warning;
+      }
+    }
+    
+    h3 {
+      font-size: 17px;
+      color: $ios-text-primary;
+      margin: 0;
+      font-family: $ios-font-family;
+      flex: 1;
+      margin-right: 10px;
+    }
+  }
+  
+  .card-content {
+    flex: 1;
+    margin-bottom: 16px;
+    
+    .project-name,
+    .deadline,
+    .countdown,
+    .workload-status {
+      font-size: 14px;
+      color: $ios-text-secondary;
+      margin: 6px 0;
+    }
+    
+    .deadline.overdue {
+      color: $ios-danger;
+    }
+    
+    .countdown {
+      color: $ios-warning;
+      font-weight: $ios-font-weight-medium;
+    }
+  }
+  
+  .card-actions {
+    display: flex;
+    gap: 10px;
+    
+    button {
+      padding: 8px 16px;
+      border: none;
+      border-radius: $ios-radius-md;
+      font-size: 14px;
+      cursor: pointer;
+      transition: all 0.3s ease;
+      font-weight: $ios-font-weight-medium;
+      font-family: $ios-font-family;
+      flex: 1;
+    }
+    
+    .btn-primary {
+      background-color: $ios-primary;
+      color: white;
+      
+      &:hover {
+        background-color: color-mix(in srgb, $ios-primary 90%, black);
+        transform: translateY(-1px);
+        box-shadow: $ios-shadow-md;
+      }
+    }
+    
+    .btn-secondary {
+      background-color: $ios-background-secondary;
+      color: $ios-text-primary;
+      border: 1px solid $ios-border;
+      
+      &:hover {
+        background-color: color-mix(in srgb, $ios-background-secondary 70%, white);
+      }
+    }
+  }
+}
+
+/* 工作量卡片特定样式 */
+.workload-card {
+  .workload-indicator {
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    margin: 20px 0;
+  }
+  
+  .workload-circle {
+    width: 120px;
+    height: 120px;
+    border-radius: 50%;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    box-shadow: $ios-shadow-md;
+    transition: all 0.3s ease;
+    
+    .workload-percentage {
+      font-size: 28px;
+      font-weight: $ios-font-weight-bold;
+      color: white;
+      font-family: $ios-font-family;
+    }
+  }
+}
+
+/* 附加信息区域样式 */
+.additional-info-section {
+  display: grid;
+  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
   gap: 24px;
+  margin-bottom: 30px;
+}
+
+.info-column {
+  background: $ios-card-background;
+  border-radius: $ios-radius-lg;
+  padding: 20px;
+  box-shadow: $ios-shadow-card;
+  border: 1px solid $ios-border;
+}
+
+/* 底部信息分层展示 */
+.bottom-sections {
+  margin-top: 30px;
+  
+  .bottom-section {
+    margin-bottom: 30px;
+  }
+}
+
+/* 特定页面视图样式 */
+.skills-view,
+.personal-view {
+  background: $ios-card-background;
+  border-radius: $ios-radius-lg;
+  padding: 24px;
+  box-shadow: $ios-shadow-card;
+  border: 1px solid $ios-border;
+}
+
+/* 紧急任务区域样式 */
+.urgent-section {
+  grid-column: 1 / -1;
+}
+
+.urgent-list {
+  display: grid;
+  gap: 16px;
+}
+
+.urgent-item {
+  background: color-mix(in srgb, $ios-danger 10%, transparent);
+  border: 1px solid $ios-danger;
+  border-radius: $ios-radius-lg;
+  padding: 16px 20px;
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  
+  .urgent-content {
+    .urgent-title {
+      font-size: 17px;
+      color: $ios-danger;
+      font-weight: $ios-font-weight-medium;
+      margin: 0 0 6px 0;
+      font-family: $ios-font-family;
+    }
+    
+    .urgent-detail {
+      font-size: 15px;
+      color: $ios-text-secondary;
+      margin: 0;
+    }
+  }
+  
+  .urgent-actions {
+    .btn-urgent-detail {
+      padding: 10px 18px;
+      border: none;
+      border-radius: $ios-radius-md;
+      background-color: $ios-danger;
+      color: white;
+      font-size: 15px;
+      cursor: pointer;
+      transition: all 0.3s ease;
+      font-weight: $ios-font-weight-medium;
+      font-family: $ios-font-family;
+      
+      &:hover {
+        background-color: color-mix(in srgb, $ios-danger 90%, black);
+        transform: translateY(-1px);
+        box-shadow: $ios-shadow-md;
+      }
+      
+      &:active {
+        transform: translateY(0);
+      }
+    }
+  }
+}
+
+/* 待办任务区域样式 */
+.task-section {
+  grid-column: 1 / -1;
+}
+
+.task-list {
+  display: grid;
+  gap: 16px;
+}
+
+.task-item {
+  background: $ios-card-background;
+  border-radius: $ios-radius-lg;
+  padding: 20px;
+  box-shadow: $ios-shadow-card;
+  transition: all 0.3s ease;
+  border: 1px solid $ios-border;
+  
+  &:hover {
+    box-shadow: $ios-shadow-lg;
+    transform: translateY(-2px);
+  }
+  
+  .task-header {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    margin-bottom: 12px;
+    
+    h3 {
+      font-size: 17px;
+      color: $ios-text-primary;
+      margin: 0;
+      font-family: $ios-font-family;
+    }
+    
+    .task-stage {
+      font-size: 13px;
+      padding: 6px 14px;
+      border-radius: $ios-radius-full;
+      background-color: $ios-primary-light;
+      color: $ios-primary;
+      border: 1px solid $ios-primary;
+    }
+  }
+  
+  .task-info {
+    margin-bottom: 16px;
+    
+    .project-name {
+      font-size: 15px;
+      color: $ios-text-secondary;
+      margin: 6px 0;
+    }
+    
+    .deadline {
+      font-size: 15px;
+      color: $ios-text-secondary;
+      margin: 6px 0;
+      
+      &.overdue {
+        color: $ios-danger;
+      }
+      
+      .overdue-badge {
+        background-color: rgba(255, 59, 48, 0.1);
+        color: $ios-danger;
+        padding: 4px 10px;
+        border-radius: $ios-radius-full;
+        font-size: 12px;
+        margin-left: 8px;
+        border: 1px solid $ios-danger;
+      }
+      
+      .countdown-badge {
+        background-color: color-mix(in srgb, $ios-warning 15%, transparent);
+        color: $ios-warning;
+        padding: 4px 10px;
+        border-radius: $ios-radius-full;
+        font-size: 12px;
+        margin-left: 8px;
+        border: 1px solid $ios-warning;
+      }
+    }
+    
+    .task-progress {
+      margin-top: 12px;
+      
+      .progress-bar {
+        height: 8px;
+        background-color: $ios-background-secondary;
+        border-radius: $ios-radius-full;
+        overflow: hidden;
+        border: 1px solid $ios-border;
+        
+        .progress-fill {
+          height: 100%;
+          background-color: $ios-primary;
+          transition: width 0.3s ease;
+          border-radius: $ios-radius-full;
+        }
+      }
+      
+      .progress-text {
+        font-size: 13px;
+        color: $ios-text-secondary;
+        margin: 6px 0 0 0;
+        text-align: right;
+      }
+    }
+  }
+  
+  .task-actions {
+    display: flex;
+    gap: 12px;
+    
+    button {
+      padding: 10px 18px;
+      border: none;
+      border-radius: $ios-radius-md;
+      font-size: 15px;
+      cursor: pointer;
+      transition: $ios-animation-normal $ios-animation-easing;
+      font-weight: $ios-font-weight-medium;
+      font-family: $ios-font-family;
+    }
+    
+    .btn-complete {
+      background-color: $ios-success;
+      color: white;
+      
+      &:hover {
+        background-color: color.adjust($ios-success, $lightness: -5%);
+        transform: translateY(-1px);
+        box-shadow: $ios-shadow-md;
+      }
+      
+      &:active {
+        transform: translateY(0);
+      }
+    }
+    
+    .btn-detail {
+      background-color: $ios-primary;
+      color: white;
+      
+      &:hover {
+        background-color: #003A8C;
+        transform: translateY(-1px);
+        box-shadow: $ios-shadow-md;
+      }
+      
+      &:active {
+        transform: translateY(0);
+      }
+    }
+  }
+}
+
+/* 待处理反馈区域样式 */
+.feedback-section {
+  grid-column: 1 / -1;
+}
+
+.feedback-list {
+  display: grid;
+  gap: 16px;
+}
+
+.feedback-item {
+  background: color-mix(in srgb, $ios-warning 10%, transparent);
+  border: 1px solid $ios-warning;
+  border-radius: $ios-radius-lg;
+  padding: 16px 20px;
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  
+  .feedback-content {
+    .feedback-title {
+      font-size: 17px;
+      color: $ios-warning;
+      font-weight: $ios-font-weight-medium;
+      margin: 0 0 6px 0;
+      font-family: $ios-font-family;
+    }
+    
+    .feedback-project,
+    .feedback-summary,
+    .feedback-time {
+      font-size: 14px;
+      color: $ios-text-secondary;
+      margin: 4px 0;
+    }
+  }
+  
+  .feedback-actions {
+    .btn-handle-feedback {
+      padding: 10px 18px;
+      border: 1px solid $ios-warning;
+      border-radius: $ios-radius-md;
+      background-color: $ios-card-background;
+      color: $ios-warning;
+      font-size: 15px;
+      cursor: pointer;
+      transition: all 0.3s ease;
+      font-weight: $ios-font-weight-medium;
+      font-family: $ios-font-family;
+      
+      &:hover {
+        background-color: $ios-warning;
+        color: white;
+        transform: translateY(-1px);
+        box-shadow: $ios-shadow-md;
+      }
+      
+      &:active {
+        transform: translateY(0);
+      }
+    }
+  }
+}
+
+/* 时间预警区域样式 */
+.warning-section {
+  grid-column: 1 / -1;
+}
+
+.warning-list {
+  display: grid;
+  gap: 16px;
+}
+
+.warning-item {
+  background: rgba(255, 149, 0, 0.1);
+  border: 1px solid $ios-warning;
+  border-radius: $ios-radius-lg;
+  padding: 16px 20px;
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  
+  .warning-content {
+    
+    .warning-title {
+      font-size: 17px;
+      color: $ios-warning;
+      font-weight: $ios-font-weight-medium;
+      margin: 0 0 6px 0;
+      font-family: $ios-font-family;
+    }
+    
+    .warning-detail {
+      font-size: 15px;
+      color: $ios-warning;
+      margin: 0;
+    }
+  }
+  
+  .warning-actions {
+    
+    .btn-generate-reminder {
+      padding: 10px 18px;
+      border: 1px solid $ios-warning;
+      border-radius: $ios-radius-md;
+      background-color: $ios-card-background;
+      color: $ios-warning;
+      font-size: 15px;
+      cursor: pointer;
+      transition: all 0.3s ease;
+      font-weight: 500;
+      font-family: $ios-font-family;
+      
+      &:hover {
+        background-color: $ios-warning;
+        color: white;
+        transform: translateY(-1px);
+        box-shadow: $ios-shadow-md;
+      }
+      
+      &:active {
+        transform: translateY(0);
+      }
+    }
+  }
+}
+
+/* 代班信息样式 */
+.shift-info {
+  background: $ios-card-background;
+  border-radius: $ios-radius-lg;
+  padding: $ios-spacing-xl;
+  box-shadow: $ios-shadow-card;
+  border: 1px solid $ios-border;
+  height: fit-content;
+}
+
+.shift-info .section-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: $ios-spacing-xl;
+}
+
+.shift-info .section-header h2 {
+  font-size: $ios-font-size-lg;
+  color: $ios-text-primary;
+  font-weight: $ios-font-weight-medium;
+  display: flex;
+  align-items: center;
+  margin: 0;
+  font-family: $ios-font-family;
+}
+
+.add-shift-btn {
+  padding: $ios-spacing-sm $ios-spacing-lg;
+  border: 1px solid $ios-primary;
+  border-radius: $ios-radius-md;
+  background-color: $ios-card-background;
+  color: $ios-primary;
+  font-size: $ios-font-size-sm;
+  cursor: pointer;
+  transition: $ios-feedback-tap;
+  font-weight: $ios-font-weight-medium;
+  font-family: $ios-font-family;
+}
+
+.add-shift-btn:hover {
+  background-color: $ios-primary;
+  color: white;
+  transform: translateY(-1px);
+  box-shadow: $ios-shadow-sm;
+}
+
+.add-shift-btn:active {
+  transform: translateY(0);
+}
+
+.shift-list {
+  display: flex;
+  flex-direction: column;
+  gap: $ios-spacing-lg;
+}
+
+.shift-item {
+  background-color: $ios-background-secondary;
+  border-radius: $ios-radius-md;
+  padding: $ios-spacing-lg;
+  border: 1px solid $ios-border;
+  transition: $ios-feedback-hover;
+}
+
+.shift-item:hover {
+  background-color: color-mix(in srgb, $ios-background-secondary 70%, white);
+  box-shadow: $ios-shadow-sm;
+}
+
+.shift-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: $ios-spacing-md;
+}
+
+.shift-project {
+  font-size: $ios-font-size-base;
+  color: $ios-text-primary;
+  font-weight: $ios-font-weight-medium;
+  font-family: $ios-font-family;
+}
+
+.shift-priority {
+  padding: $ios-spacing-xs $ios-spacing-sm;
+  border-radius: $ios-radius-full;
+  font-size: $ios-font-size-xs;
+  font-weight: $ios-font-weight-medium;
+  text-transform: uppercase;
+  font-family: $ios-font-family;
+}
+
+.shift-priority.priority-high {
+  background-color: color-mix(in srgb, $ios-danger 15%, transparent);
+  color: $ios-danger;
+  border: 1px solid $ios-danger;
+}
+
+.shift-priority.priority-medium {
+  background-color: color-mix(in srgb, $ios-warning 15%, transparent);
+  color: $ios-warning;
+  border: 1px solid $ios-warning;
+}
+
+.shift-priority.priority-low {
+  background-color: color-mix(in srgb, $ios-success 15%, transparent);
+  color: $ios-success;
+  border: 1px solid $ios-success;
+}
+
+.shift-details {
+  margin-bottom: $ios-spacing-lg;
+}
+
+.shift-task {
+  font-size: $ios-font-size-sm;
+  color: $ios-text-secondary;
+  margin-bottom: $ios-spacing-xs;
+  line-height: 1.4;
+}
+
+.shift-time {
+  font-size: $ios-font-size-xs;
+  color: $ios-text-tertiary;
+}
+
+/* 响应式设计补充 */
+@media (max-width: 768px) {
+  .dashboard-header h1 {
+    font-size: 24px;
+  }
+  
+  .dashboard-nav {
+    flex-direction: column;
+  }
+  
+  .nav-btn {
+    padding: 10px 16px;
+  }
+  
+  .core-cards-section .cards-grid {
+    grid-template-columns: 1fr;
+    gap: 16px;
+  }
+  
+  .additional-info-section {
+    grid-template-columns: 1fr;
+    gap: 16px;
+  }
+  
+  .core-card {
+    padding: 16px;
+  }
+  
+  .core-card h3 {
+    font-size: 16px;
+  }
+  
+  .workload-circle {
+    width: 100px !important;
+    height: 100px !important;
+  }
+  
+  .workload-percentage {
+    font-size: 24px !important;
+  }
+}
+
+/* iOS风格滚动条 */
+.dashboard-container {
+  &::-webkit-scrollbar {
+    width: 8px;
+    height: 8px;
+  }
+  
+  &::-webkit-scrollbar-track {
+    background: $ios-background-secondary;
+    border-radius: $ios-radius-full;
+  }
+  
+  &::-webkit-scrollbar-thumb {
+    background: $ios-scrollbar-thumb;
+    border: 2px solid $ios-background-secondary;
+  }
   
-  .task-section,
-  .urgent-section,
-  .feedback-section,
-  .warning-section,
-  .quick-access-section {
-    grid-column: 1 / -1;
+  &::-webkit-scrollbar-thumb:hover {
+    background: $ios-scrollbar-thumb-hover;
   }
 }
 

+ 46 - 11
src/app/pages/designer/dashboard/dashboard.ts

@@ -4,6 +4,7 @@ import { RouterModule } from '@angular/router';
 import { ProjectService } from '../../../services/project.service';
 import { Task } from '../../../models/project.model';
 import { SkillRadarComponent } from './skill-radar/skill-radar.component';
+import { PersonalBoard } from '../personal-board/personal-board';
 
 interface ShiftTask {
   id: string;
@@ -24,11 +25,14 @@ interface ProjectTimelineItem {
 
 @Component({
   selector: 'app-dashboard',
-  imports: [CommonModule, RouterModule, SkillRadarComponent],
+  imports: [CommonModule, RouterModule, SkillRadarComponent, PersonalBoard],
   templateUrl: './dashboard.html',
   styleUrl: './dashboard.scss'
 })
 export class Dashboard implements OnInit {
+  // 视图管理
+  activeDashboard: 'main' | 'skills' | 'personal' = 'main';
+  
   tasks: Task[] = [];
   overdueTasks: Task[] = [];
   urgentTasks: Task[] = [];
@@ -52,6 +56,47 @@ export class Dashboard implements OnInit {
     this.calculateWorkloadPercentage();
     this.loadProjectTimeline();
   }
+  
+  // 切换视图方法
+  switchDashboard(view: 'main' | 'skills' | 'personal'): void {
+    this.activeDashboard = view;
+  }
+  
+  // 获取前N个任务的方法
+  getTopTasks(count: number): Task[] {
+    // 过滤掉紧急任务和超期任务
+    const regularTasks = this.tasks.filter(task => 
+      !this.urgentTasks.some(urgent => urgent.id === task.id) &&
+      !this.overdueTasks.some(overdue => overdue.id === task.id)
+    );
+    
+    // 返回指定数量的任务
+    return regularTasks.slice(0, count);
+  }
+  
+  // 获取工作量颜色的方法
+  getWorkloadColor(): string {
+    if (this.workloadPercentage >= 80) {
+      return '#ff4560'; // 红色
+    } else if (this.workloadPercentage >= 50) {
+      return '#ffa726'; // 橙色
+    } else {
+      return '#66bb6a'; // 绿色
+    }
+  }
+  
+  // 获取工作量状态的方法
+  getWorkloadStatus(): string {
+    if (this.workloadPercentage >= 80) {
+      return '工作量饱和';
+    } else if (this.workloadPercentage >= 50) {
+      return '工作量适中';
+    } else if (this.workloadPercentage > 0) {
+      return '工作量轻松';
+    } else {
+      return '暂无工作任务';
+    }
+  }
 
   loadTasks(): void {
     this.projectService.getTasks().subscribe(tasks => {
@@ -340,14 +385,4 @@ export class Dashboard implements OnInit {
     ].sort((a, b) => new Date(a.deadline).getTime() - new Date(b.deadline).getTime());
   }
 
-  // 获取项目饱和度状态文本
-  getWorkloadStatus(): string {
-    if (this.workloadPercentage < 75) {
-      return '工作负载正常';
-    } else if (this.workloadPercentage < 90) {
-      return '工作负载较高';
-    } else {
-      return '工作负载已满';
-    }
-  }
 }

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

@@ -0,0 +1,103 @@
+/* 调试样式文件 - 增强版本,确保布局正确显示 */
+
+/* 重置所有可能冲突的样式 - 使用最高优先级 */
+* {
+  box-sizing: border-box !important;
+}
+
+/* 重置主内容区域样式 */
+.main-content {
+  width: 100% !important;
+  margin: 0 !important;
+  padding: 0 !important;
+}
+
+/* 重置内容包装器样式 */
+.content-wrapper {
+  width: 100% !important;
+  margin: 0 !important;
+  padding: 0 !important;
+}
+
+/* 重置项目详情容器 */
+.project-detail-container {
+  padding: 20px !important;
+  width: 100% !important;
+  margin: 0 !important;
+}
+
+/* 重置标签页内容容器 */
+.tab-content {
+  width: 100% !important;
+  padding: 0 !important;
+  margin: 0 !important;
+}
+
+/* 重置项目进度标签页内容 */
+.progress-tab-content {
+  width: 100% !important;
+  padding: 0 !important;
+  margin: 0 !important;
+}
+
+/* 强制覆盖主内容布局样式 - 使用最高优先级 */
+.progress-tab-content > .main-content-layout {
+  display: flex !important;
+  flex-wrap: nowrap !important;
+  gap: 20px !important;
+  margin-top: 20px !important;
+  width: 100% !important;
+  background-color: rgba(200, 200, 255, 0.3) !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;
+  display: flex !important;
+  flex-direction: column !important;
+  gap: 20px !important;
+  background-color: rgba(255, 200, 200, 0.3) !important; /* 左侧列背景色 */
+  padding: 10px !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;
+  display: flex !important;
+  flex-direction: column !important;
+  gap: 20px !important;
+  background-color: rgba(200, 255, 200, 0.3) !important; /* 右侧列背景色 */
+  padding: 10px !important;
+  border-radius: 6px !important;
+}
+
+/* 优化卡片样式 */
+.left-column .card,
+.right-column .card {
+  background-color: white !important;
+  border-radius: 8px !important;
+  padding: 20px !important;
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1) !important;
+  border: 1px solid #eaeaea !important;
+}
+
+/* 确保响应式布局正常工作 */
+@media (max-width: 1024px) {
+  .progress-tab-content > .main-content-layout {
+    flex-direction: column !important;
+  }
+  
+  .progress-tab-content > .main-content-layout > .left-column,
+  .progress-tab-content > .main-content-layout > .right-column {
+    width: 100% !important;
+    min-width: 100% !important;
+    max-width: 100% !important;
+  }
+}

+ 525 - 327
src/app/pages/designer/project-detail/project-detail.html

@@ -1,4 +1,4 @@
-<div class="project-detail-container">
+<div class="project-detail-container designer-page">
   <!-- 项目标题栏 -->
   <div class="project-header card">
     <div class="header-content">
@@ -40,380 +40,578 @@
     {{ reminderMessage }}
   </div>
 
-  <!-- 主内容区 - 网格布局 -->
-  <div class="main-content-grid">
-    <!-- 项目基本信息卡片 -->
-    <div class="project-info-card card">
-      <h2>项目基本信息</h2>
-      <div class="info-grid">
-        <div class="info-item">
-          <label>项目名称</label>
-          <span>{{ project?.name || '加载中...' }}</span>
-        </div>
-        <div class="info-item">
-          <label>客户姓名</label>
-          <span>{{ project?.customerName || '加载中...' }}</span>
-        </div>
-        <div class="info-item">
-          <label>当前阶段</label>
-          <span class="stage-tag">{{ project?.currentStage || '加载中...' }}</span>
-        </div>
-        <div class="info-item" *ngIf="project">
-          <label>预计交付日期</label>
-          <span>{{ project.deadline | date:'yyyy-MM-dd' }}</span>
-        </div>
-      </div>
-    </div>
+  <!-- 顶部导航标签页 -->
+  <!-- <div class="project-tabs">
+    <div class="tab-header">
+      <button (click)="switchTab('progress')" [class.active]="isActiveTab('progress')" class="tab-btn">
+        <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>
+        <span>项目进度</span>
+      </button>
+      <button (click)="switchTab('members')" [class.active]="isActiveTab('members')" class="tab-btn">
+        <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.87"></path>
+          <path d="M16 3.13a4 4 0 0 1 0 7.75"></path>
+        </svg>
+        <span>项目成员</span>
+      </button>
+      <button (click)="switchTab('files')" [class.active]="isActiveTab('files')" class="tab-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>
+          <polyline points="10 9 9 9 8 9"></polyline>
+        </svg>
+        <span>项目文件</span>
+      </button>
+    </div> -->
 
-    <!-- 客户画像卡片 -->
-    <div class="customer-profile-card card">
-      <h2>客户画像</h2>
-      
-      <!-- 技能匹配度警告 -->
-      <div *ngIf="getSkillMismatchWarning()" class="warning-banner">
-        <div class="warning-content">
-          <span class="warning-icon">⚠️</span>
-          <span class="warning-text">{{ getSkillMismatchWarning() }}</span>
-        </div>
-        <button (click)="notifyTeamLeader('skill-mismatch')" class="contact-leader-btn">联系组长</button>
-      </div>
-    
-      <div *ngIf="project" class="tags-container">
-        <div class="tag-section">
-          <h3>客户偏好</h3>
-          <div class="tags-grid">
-            <ng-container *ngIf="project.customerTags && project.customerTags.length > 0">
-              <div class="tag-item">
-                <span class="tag-label">需求类型</span>
-                <span *ngIf="project.customerTags[0].needType" class="tag">
-                  {{ project.customerTags[0].needType }}
-                </span>
+    <!-- 标签页内容 -->
+    <div class="tab-content">
+      <!-- 项目进度标签页 -->
+      <div *ngIf="isActiveTab('progress')" class="progress-tab-content">
+        <!-- 主要内容布局 - 左侧三分之一,右侧三分之二 -->
+        <div class="main-content-layout">
+          <!-- 左侧三分之一 - 项目基本信息和客户画像 -->
+          <div class="left-column">
+            <!-- 项目基本信息 -->
+            <div class="project-info-card card">
+              <h2>项目基本信息</h2>
+              <div class="info-grid">
+                <div class="info-item">
+                  <label>项目名称</label>
+                  <span>{{ project?.name || '加载中...' }}</span>
+                </div>
+                <div class="info-item">
+                  <label>客户姓名</label>
+                  <span>{{ project?.customerName || '加载中...' }}</span>
+                </div>
+                <div class="info-item">
+                  <label>当前阶段</label>
+                  <span class="stage-tag">{{ project?.currentStage || '加载中...' }}</span>
+                </div>
+                <div class="info-item" *ngIf="project">
+                  <label>预计交付日期</label>
+                  <span>{{ project.deadline | date:'yyyy-MM-dd' }}</span>
+                </div>
               </div>
-              <div class="tag-item">
-                <span class="tag-label">设计风格</span>
-                <span *ngIf="project.customerTags[0].preference" class="tag">
-                  {{ project.customerTags[0].preference }}
-                </span>
+            </div>
+
+            <!-- 客户画像 -->
+            <div class="customer-profile-card card">
+              <h2>客户画像</h2>
+              
+              <!-- 技能匹配度警告 -->
+              <div *ngIf="getSkillMismatchWarning()" class="warning-banner">
+                <div class="warning-content">
+                  <span class="warning-icon">⚠️</span>
+                  <span class="warning-text">{{ getSkillMismatchWarning() }}</span>
+                </div>
+                <button (click)="notifyTeamLeader('skill-mismatch')" class="contact-leader-btn">联系组长</button>
               </div>
-              <div class="tag-item">
-                <span class="tag-label">色彩氛围</span>
-                <span *ngIf="project.customerTags[0].colorAtmosphere" class="tag">
-                  {{ project.customerTags[0].colorAtmosphere }}
-                </span>
+            
+              <div *ngIf="project" class="tags-container">
+                <div class="tag-section">
+                  <h3>客户偏好</h3>
+                  <div class="tags-grid">
+                    <ng-container *ngIf="project.customerTags && project.customerTags.length > 0">
+                      <div class="tag-item">
+                        <span class="tag-label">需求类型</span>
+                        <span *ngIf="project.customerTags[0].needType" class="tag">
+                          {{ project.customerTags[0].needType }}
+                        </span>
+                      </div>
+                      <div class="tag-item">
+                        <span class="tag-label">设计风格</span>
+                        <span *ngIf="project.customerTags[0].preference" class="tag">
+                          {{ project.customerTags[0].preference }}
+                        </span>
+                      </div>
+                      <div class="tag-item">
+                        <span class="tag-label">色彩氛围</span>
+                        <span *ngIf="project.customerTags[0].colorAtmosphere" class="tag">
+                          {{ project.customerTags[0].colorAtmosphere }}
+                        </span>
+                      </div>
+                    </ng-container>
+                  </div>
+                </div>
+                
+                <div class="tag-section">
+                  <h3>项目要求</h3>
+                  <div class="tags-flex">
+                    <div class="tag-group">
+                      <span class="group-label">高优先级需求</span>
+                      <div class="tags">
+                        <span *ngFor="let priority of project.highPriorityNeeds" class="priority-tag">
+                          {{ priority }}
+                        </span>
+                      </div>
+                    </div>
+                    <div class="tag-group">
+                      <span class="group-label">擅长技能</span>
+                      <div class="tags">
+                        <span *ngFor="let skill of project.skillsRequired" class="skill-tag">
+                          {{ skill }}
+                        </span>
+                      </div>
+                    </div>
+                  </div>
+                </div>
               </div>
-            </ng-container>
+            </div>
           </div>
-        </div>
-        
-        <div class="tag-section">
-          <h3>项目要求</h3>
-          <div class="tags-flex">
-            <div class="tag-group">
-              <span class="group-label">高优先级需求</span>
-              <div class="tags">
-                <span *ngFor="let priority of project.highPriorityNeeds" class="priority-tag">
-                  {{ priority }}
-                </span>
+
+          <!-- 右侧三分之二 - 制作流程进度 -->
+          <div class="right-column">
+            <div class="process-card card">
+              <h2>制作流程进度</h2>
+              <div class="stage-progress-container">
+                <div class="stage-progress">
+                  <div class="stage" [class.completed]="project?.currentStage !== '建模'" [class.active]="project?.currentStage === '建模'">
+                    <div class="stage-icon">
+                      <span>{{ project?.currentStage !== '建模' ? '✓' : '1' }}</span>
+                    </div>
+                    <div class="stage-name">建模</div>
+                  </div>
+                  <div class="progress-line"></div>
+                  <div class="stage" [class.completed]="project?.currentStage !== '软装' && project?.currentStage !== '建模'" [class.active]="project?.currentStage === '软装'">
+                    <div class="stage-icon">
+                      <span>{{ project?.currentStage !== '软装' && project?.currentStage !== '建模' ? '✓' : '2' }}</span>
+                    </div>
+                    <div class="stage-name">软装</div>
+                  </div>
+                  <div class="progress-line"></div>
+                  <div class="stage" [class.completed]="project?.currentStage !== '渲染' && project?.currentStage !== '建模' && project?.currentStage !== '软装'" [class.active]="project?.currentStage === '渲染'">
+                    <div class="stage-icon">
+                      <span>{{ project?.currentStage !== '渲染' && project?.currentStage !== '建模' && project?.currentStage !== '软装' ? '✓' : '3' }}</span>
+                    </div>
+                    <div class="stage-name">渲染</div>
+                  </div>
+                  <div class="progress-line"></div>
+                  <div class="stage" [class.completed]="project?.currentStage !== '后期' && project?.currentStage !== '建模' && project?.currentStage !== '软装' && project?.currentStage !== '渲染'" [class.active]="project?.currentStage === '后期'">
+                    <div class="stage-icon">
+                      <span>{{ project?.currentStage !== '后期' && project?.currentStage !== '建模' && project?.currentStage !== '软装' && project?.currentStage !== '渲染' ? '✓' : '4' }}</span>
+                    </div>
+                    <div class="stage-name">后期</div>
+                  </div>
+                </div>
               </div>
-            </div>
-            <div class="tag-group">
-              <span class="group-label">擅长技能</span>
-              <div class="tags">
-                <span *ngFor="let skill of project.skillsRequired" class="skill-tag">
-                  {{ skill }}
-                </span>
+              
+              <!-- 当前阶段操作 -->
+              <div *ngIf="project" class="current-stage-actions">
+                <div class="current-stage-info">
+                  <h3>当前阶段: <span class="stage-highlight">{{ project.currentStage }}</span></h3>
+                </div>
+                <div class="stage-actions">
+                  <button *ngIf="project.currentStage === '建模'" (click)="updateProjectStage('软装')" [disabled]="!areAllModelChecksPassed()" class="primary-btn">
+                    {{ areAllModelChecksPassed() ? '完成建模' : '完成所有模型检查' }}
+                  </button>
+                  <button *ngIf="project.currentStage === '软装'" (click)="updateProjectStage('渲染')" class="primary-btn">完成软装</button>
+                  <button *ngIf="project.currentStage === '渲染'" (click)="updateProjectStage('后期')" class="primary-btn">完成渲染</button>
+                  <button *ngIf="project.currentStage === '后期'" (click)="updateProjectStage('完成')" class="primary-btn">完成后期</button>
+                </div>
               </div>
             </div>
           </div>
         </div>
-      </div>
-    </div>
 
-    <!-- 制作流程进度卡片 -->
-    <div class="process-card card">
-      <h2>制作流程进度</h2>
-      <div class="stage-progress-container">
-        <div class="stage-progress">
-          <div class="stage" [class.completed]="project?.currentStage !== '建模'" [class.active]="project?.currentStage === '建模'">
-            <div class="stage-icon">
-              <span>{{ project?.currentStage !== '建模' ? '✓' : '1' }}</span>
+        <!-- 阶段专属任务卡片 - 仅在对应节点显示 -->
+        <div class="stage-specific-cards">
+          <!-- 误查检查单卡片 - 根据shouldShowCard方法在特定阶段显示 -->
+          <div *ngIf="shouldShowCard('modelCheck')" class="model-check-card card">
+            <h2>模型误差检查清单</h2>
+            <div class="checklist">
+              <div *ngFor="let item of modelCheckItems" class="checklist-item">
+                <input type="checkbox" [checked]="item.isPassed" (change)="updateModelCheckItem(item.id, !item.isPassed)" class="custom-checkbox">
+                <span class="checklist-text">{{ item.name }}</span>
+                <span class="check-status" [class.passed]="item.isPassed" [class.failed]="!item.isPassed">
+                  {{ item.isPassed ? '通过' : '未通过' }}
+                </span>
+              </div>
             </div>
-            <div class="stage-name">建模</div>
           </div>
-          <div class="progress-line"></div>
-          <div class="stage" [class.completed]="project?.currentStage !== '软装' && project?.currentStage !== '建模'" [class.active]="project?.currentStage === '软装'">
-            <div class="stage-icon">
-              <span>{{ project?.currentStage !== '软装' && project?.currentStage !== '建模' ? '✓' : '2' }}</span>
+
+          <!-- 渲染阶段专属卡片 -->
+          <div *ngIf="project?.currentStage === '渲染'" class="render-progress-card card">
+            <h2>渲染进度</h2>
+            <div *ngIf="isLoadingRenderProgress" class="loading-state">
+              <div class="loading-spinner"></div>
+              <span>加载中...</span>
             </div>
-            <div class="stage-name">软装</div>
-          </div>
-          <div class="progress-line"></div>
-          <div class="stage" [class.completed]="project?.currentStage !== '渲染' && project?.currentStage !== '建模' && project?.currentStage !== '软装'" [class.active]="project?.currentStage === '渲染'">
-            <div class="stage-icon">
-              <span>{{ project?.currentStage !== '渲染' && project?.currentStage !== '建模' && project?.currentStage !== '软装' ? '✓' : '3' }}</span>
+            <div *ngIf="errorLoadingRenderProgress" class="error-state">
+              <span>加载失败</span>
+              <button (click)="retryLoadRenderProgress()" class="secondary-btn">点击重试</button>
             </div>
-            <div class="stage-name">渲染</div>
-          </div>
-          <div class="progress-line"></div>
-          <div class="stage" [class.completed]="project?.currentStage !== '后期' && project?.currentStage !== '建模' && project?.currentStage !== '软装' && project?.currentStage !== '渲染'" [class.active]="project?.currentStage === '后期'">
-            <div class="stage-icon">
-              <span>{{ project?.currentStage !== '后期' && project?.currentStage !== '建模' && project?.currentStage !== '软装' && project?.currentStage !== '渲染' ? '✓' : '4' }}</span>
+            <div *ngIf="renderProgress && !isLoadingRenderProgress && !errorLoadingRenderProgress" class="progress-content">
+              <!-- 渲染超时预警 -->
+            <div *ngIf="renderProgress.estimatedTimeRemaining <= 3" class="timeout-warning">
+              <div class="warning-icon">⚠️</div>
+              <div class="warning-text">
+                <span class="warning-title">渲染即将超时</span>
+                <span class="warning-time">预计剩余时间: {{ renderProgress.estimatedTimeRemaining }} 小时</span>
+              </div>
             </div>
-            <div class="stage-name">后期</div>
-          </div>
-        </div>
-      </div>
-      
-      <!-- 当前阶段操作 -->
-      <div *ngIf="project" class="current-stage-actions">
-        <div class="current-stage-info">
-          <h3>当前阶段: <span class="stage-highlight">{{ project.currentStage }}</span></h3>
-        </div>
-        <div class="stage-actions">
-          <button *ngIf="project.currentStage === '建模'" (click)="updateProjectStage('软装')" [disabled]="!areAllModelChecksPassed()" class="primary-btn">
-            {{ areAllModelChecksPassed() ? '完成建模' : '完成所有模型检查' }}
-          </button>
-          <button *ngIf="project.currentStage === '软装'" (click)="updateProjectStage('渲染')" class="primary-btn">完成软装</button>
-          <button *ngIf="project.currentStage === '渲染'" (click)="updateProjectStage('后期')" class="primary-btn">完成渲染</button>
-          <button *ngIf="project.currentStage === '后期'" (click)="updateProjectStage('完成')" class="primary-btn">完成后期</button>
-        </div>
-      </div>
-    </div>
 
-    <!-- 渲染进度卡片 -->
-    <div class="render-progress-card card">
-      <h2>渲染进度</h2>
-      <div *ngIf="isLoadingRenderProgress" class="loading-state">
-        <div class="loading-spinner"></div>
-        <span>加载中...</span>
-      </div>
-      <div *ngIf="errorLoadingRenderProgress" class="error-state">
-        <span>加载失败</span>
-        <button (click)="retryLoadRenderProgress()" class="secondary-btn">点击重试</button>
-      </div>
-      <div *ngIf="renderProgress && !isLoadingRenderProgress && !errorLoadingRenderProgress" class="progress-content">
-        <!-- 渲染超时预警 -->
-      <div *ngIf="renderProgress.estimatedTimeRemaining <= 3" class="timeout-warning">
-        <div class="warning-icon">⚠️</div>
-        <div class="warning-text">
-          <span class="warning-title">渲染即将超时</span>
-          <span class="warning-time">预计剩余时间: {{ renderProgress.estimatedTimeRemaining }} 小时</span>
-        </div>
-      </div>
+            <!-- 渲染异常反馈模块 -->
+            <div class="render-exception-section">
+              <h3>渲染异常反馈</h3>
+              <div class="exception-feedback-form">
+                <div class="form-group">
+                  <label>异常类型:</label>
+                  <select [(ngModel)]="exceptionType" class="exception-select">
+                    <option value="failed">渲染失败</option>
+                    <option value="stuck">渲染卡顿</option>
+                    <option value="quality">渲染质量问题</option>
+                    <option value="other">其他问题</option>
+                  </select>
+                </div>
+                <div class="form-group">
+                  <label>详细描述:</label>
+                  <textarea 
+                    [(ngModel)]="exceptionDescription" 
+                    placeholder="请描述渲染过程中遇到的具体问题..."
+                    class="exception-textarea"
+                  ></textarea>
+                </div>
+                <div class="form-group">
+                  <label>上传截图 (可选):</label>
+                  <input type="file" (change)="uploadExceptionScreenshot($event)" class="screenshot-upload" id="screenshot-upload">
+                  <label for="screenshot-upload" class="upload-btn">选择文件</label>
+                  <div *ngIf="exceptionScreenshotUrl" class="screenshot-preview">
+                    <img [src]="exceptionScreenshotUrl" alt="异常截图">
+                    <button (click)="clearExceptionScreenshot()" class="clear-screenshot-btn">×</button>
+                  </div>
+                </div>
+                <button 
+                  (click)="submitExceptionFeedback()" 
+                  [disabled]="!exceptionDescription.trim()"
+                  class="submit-feedback-btn"
+                >
+                  提交反馈并联系技术支持
+                </button>
+              </div>
 
-      <!-- 渲染异常反馈模块 -->
-      <div class="render-exception-section">
-        <h3>渲染异常反馈</h3>
-        <div class="exception-feedback-form">
-          <div class="form-group">
-            <label>异常类型:</label>
-            <select [(ngModel)]="exceptionType" class="exception-select">
-              <option value="failed">渲染失败</option>
-              <option value="stuck">渲染卡顿</option>
-              <option value="quality">渲染质量问题</option>
-              <option value="other">其他问题</option>
-            </select>
-          </div>
-          <div class="form-group">
-            <label>详细描述:</label>
-            <textarea 
-              [(ngModel)]="exceptionDescription" 
-              placeholder="请描述渲染过程中遇到的具体问题..."
-              class="exception-textarea"
-            ></textarea>
-          </div>
-          <div class="form-group">
-            <label>上传截图 (可选):</label>
-            <input type="file" (change)="uploadExceptionScreenshot($event)" class="screenshot-upload" id="screenshot-upload">
-            <label for="screenshot-upload" class="upload-btn">选择文件</label>
-            <div *ngIf="exceptionScreenshotUrl" class="screenshot-preview">
-              <img [src]="exceptionScreenshotUrl" alt="异常截图">
-              <button (click)="clearExceptionScreenshot()" class="clear-screenshot-btn">×</button>
+              <!-- 历史反馈记录 -->
+              <div class="exception-history" *ngIf="exceptionHistories.length > 0">
+                <h4>历史反馈记录</h4>
+                <div class="history-list">
+                  <div *ngFor="let history of exceptionHistories" class="history-item">
+                    <div class="history-header">
+                      <span class="history-type">{{ getExceptionTypeText(history.type) }}</span>
+                      <span class="history-time">{{ formatDate(history.submitTime) }}</span>
+                    </div>
+                    <div class="history-description">{{ history.description }}</div>
+                    <div class="history-status" [class.status-pending]="history.status === '待处理'" [class.status-processing]="history.status === '处理中'" [class.status-resolved]="history.status === '已解决'">
+                      {{ history.status }} - {{ history.response || '暂无回复' }}
+                    </div>
+                  </div>
+                </div>
+              </div>
+            </div>
+            
+            <div class="progress-bar-container">
+              <div class="progress-bar">
+                <div class="progress-fill" [style.width.percent]="renderProgress.completionRate"></div>
+              </div>
+              <div class="progress-percentage">{{ renderProgress.completionRate }}%</div>
+            </div>
+            
+            <div class="progress-details">
+              <div class="progress-info">
+                <span class="info-label">预计剩余时间</span>
+                <span class="info-value">{{ renderProgress.estimatedTimeRemaining }} 小时</span>
+              </div>
+              <div class="progress-info">
+                <span class="info-label">当前状态</span>
+                <span class="info-value">{{ renderProgress.status }}</span>
+              </div>
             </div>
           </div>
-          <button 
-            (click)="submitExceptionFeedback()" 
-            [disabled]="!exceptionDescription.trim()"
-            class="submit-feedback-btn"
-          >
-            提交反馈并联系技术支持
-          </button>
         </div>
 
-        <!-- 历史反馈记录 -->
-        <div class="exception-history" *ngIf="exceptionHistories.length > 0">
-          <h4>历史反馈记录</h4>
-          <div class="history-list">
-            <div *ngFor="let history of exceptionHistories" class="history-item">
-              <div class="history-header">
-                <span class="history-type">{{ getExceptionTypeText(history.type) }}</span>
-                <span class="history-time">{{ formatDate(history.submitTime) }}</span>
+        <!-- 客户反馈和设计师变更记录 -->
+        <div class="additional-info-section">
+          <div class="feedback-card card">
+            <h2>客户反馈</h2>
+            <div *ngIf="feedbacks.length === 0" class="empty-state">
+              <div class="empty-icon">📭</div>
+              <span>暂无客户反馈</span>
+            </div>
+            <div *ngFor="let feedback of feedbacks" class="feedback-item">
+              <div class="feedback-header">
+                <div class="feedback-meta">
+                  <span class="feedback-type">{{ feedback.isSatisfied ? '满意反馈' : '不满意反馈' }}</span>
+                  <span *ngIf="getFeedbackTag(feedback)" class="feedback-tag">{{ getFeedbackTag(feedback) }}</span>
+                </div>
+                <div class="feedback-date">{{ feedback.createdAt | date:'yyyy-MM-dd HH:mm' }}</div>
+              </div>
+              <div class="feedback-content">
+                <div class="feedback-status"><span class="status-label">状态:</span> <span class="status-value">{{ feedback.status }}</span></div>
+                <!-- 反馈倒计时 -->
+                <div *ngIf="feedback.status === '待处理' && feedbackTimeoutCountdown > 0" class="feedback-countdown">
+                  <span class="countdown-icon">⏱️</span>
+                  <span>响应倒计时: {{ formatCountdown(feedbackTimeoutCountdown) }}</span>
+                </div>
+                <div class="feedback-details">
+                  <div class="detail-item">
+                    <span class="detail-label">修改部位:</span>
+                    <span class="detail-value">{{ feedback.problemLocation || '-' }}</span>
+                  </div>
+                  <div class="detail-item">
+                    <span class="detail-label">期望效果:</span>
+                    <span class="detail-value">{{ feedback.expectedEffect || '-' }}</span>
+                  </div>
+                  <div class="detail-item">
+                    <span class="detail-label">参考案例:</span>
+                    <span class="detail-value">{{ feedback.referenceCase || '-' }}</span>
+                  </div>
+                </div>
               </div>
-              <div class="history-description">{{ history.description }}</div>
-              <div class="history-status" [class.status-pending]="history.status === '待处理'" [class.status-processing]="history.status === '处理中'" [class.status-resolved]="history.status === '已解决'">
-                {{ history.status }} - {{ history.response || '暂无回复' }}
+              <div class="feedback-actions">
+                <button (click)="updateFeedbackStatus(feedback.id, '处理中')" [disabled]="feedback.status === '处理中' || feedback.status === '已解决'" class="secondary-btn">
+                  标记为处理中
+                </button>
+                <button (click)="updateFeedbackStatus(feedback.id, '已解决')" [disabled]="feedback.status === '已解决'" class="primary-btn">
+                  标记为已解决
+                </button>
               </div>
             </div>
           </div>
-        </div>
-      </div>
-        
-        <div class="progress-bar-container">
-          <div class="progress-bar">
-            <div class="progress-fill" [style.width.percent]="renderProgress.completionRate"></div>
-          </div>
-          <div class="progress-percentage">{{ renderProgress.completionRate }}%</div>
-        </div>
-        
-        <div class="progress-details">
-          <div class="progress-info">
-            <span class="info-label">预计剩余时间</span>
-            <span class="info-value">{{ renderProgress.estimatedTimeRemaining }} 小时</span>
-          </div>
-          <div class="progress-info">
-            <span class="info-label">当前状态</span>
-            <span class="info-value">{{ renderProgress.status }}</span>
-          </div>
-        </div>
-      </div>
-    </div>
 
-    <!-- 模型误差检查清单卡片 -->
-    <div class="model-check-card card">
-      <h2>模型误差检查清单</h2>
-      <div class="checklist">
-        <div *ngFor="let item of modelCheckItems" class="checklist-item">
-          <input type="checkbox" [checked]="item.isPassed" (change)="updateModelCheckItem(item.id, !item.isPassed)" class="custom-checkbox">
-          <span class="checklist-text">{{ item.name }}</span>
-          <span class="check-status" [class.passed]="item.isPassed" [class.failed]="!item.isPassed">
-            {{ item.isPassed ? '通过' : '未通过' }}
-          </span>
+          <div class="designer-change-card card">
+            <h2>设计师变更记录</h2>
+            <div class="change-actions">
+              <button (click)="initiateDesignerChange('技能不匹配')" class="secondary-btn">发起变更 - 技能不匹配</button>
+              <button (click)="initiateDesignerChange('休假')" class="secondary-btn">发起变更 - 休假</button>
+            </div>
+            <div *ngIf="designerChanges.length === 0" class="empty-state">
+              <div class="empty-icon">👤</div>
+              <span>暂无设计师变更记录</span>
+            </div>
+            <div *ngFor="let change of designerChanges" class="change-item">
+              <div class="change-header">
+                <div class="change-time">{{ change.changeTime | date:'yyyy-MM-dd' }}</div>
+                <button *ngIf="!change.acceptanceTime" (click)="acceptDesignerChange(change.id)" class="accept-change-btn primary-btn">
+                  确认承接
+                </button>
+              </div>
+              <div class="change-details">
+                <div class="designer-change-info">
+                  <div class="designer-change">
+                    <span class="designer-label">原设计师:</span>
+                    <span class="designer-name">{{ change.oldDesignerName }}</span>
+                  </div>
+                  <div class="designer-change">
+                    <span class="designer-label">新设计师:</span>
+                    <span class="designer-name">{{ change.newDesignerName }}</span>
+                  </div>
+                </div>
+                <div class="workload-info">
+                  <span>已完成工作量: <strong>{{ change.completedWorkload }}%</strong></span>
+                </div>
+                <div class="achievements">
+                  <h4>历史阶段成果:</h4>
+                  <ul>
+                    <li *ngFor="let achievement of change.historicalAchievements">{{ achievement }}</li>
+                  </ul>
+                </div>
+                <div *ngIf="change.acceptanceTime" class="change-status">
+                  承接确认时间: {{ change.acceptanceTime | date:'yyyy-MM-dd' }}
+                </div>
+              </div>
+            </div>
+          </div>
         </div>
       </div>
-    </div>
 
-    <!-- 客户反馈卡片 -->
-    <div class="feedback-card card">
-      <h2>客户反馈</h2>
-      <div *ngIf="feedbacks.length === 0" class="empty-state">
-        <div class="empty-icon">📭</div>
-        <span>暂无客户反馈</span>
-      </div>
-      <div *ngFor="let feedback of feedbacks" class="feedback-item">
-        <div class="feedback-header">
-          <div class="feedback-meta">
-            <span class="feedback-type">{{ feedback.isSatisfied ? '满意反馈' : '不满意反馈' }}</span>
-            <span *ngIf="getFeedbackTag(feedback)" class="feedback-tag">{{ getFeedbackTag(feedback) }}</span>
+      <!-- 项目成员标签页 -->
+      <div *ngIf="isActiveTab('members')" class="members-tab-content">
+        <div class="project-members-card card">
+          <h2>项目团队成员</h2>
+          <div class="members-grid">
+            <div *ngFor="let member of projectMembers" class="member-card">
+              <div class="member-avatar">
+                <div class="avatar-placeholder">{{ member.avatar }}</div>
+              </div>
+              <div class="member-info">
+                <div class="member-name">{{ member.name }}</div>
+                <div class="member-role">{{ member.role }}</div>
+                <div class="member-skills">
+                  <span *ngIf="member.skillMatch >= 90" class="skill-badge">技能匹配良好</span>
+                  <span *ngIf="member.progress >= 80" class="skill-badge">进度领先</span>
+                </div>
+              </div>
+              <div class="member-actions">
+                <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="feedback-date">{{ feedback.createdAt | date:'yyyy-MM-dd HH:mm' }}</div>
         </div>
-        <div class="feedback-content">
-          <div class="feedback-status"><span class="status-label">状态:</span> <span class="status-value">{{ feedback.status }}</span></div>
-          <!-- 反馈倒计时 -->
-          <div *ngIf="feedback.status === '待处理' && feedbackTimeoutCountdown > 0" class="feedback-countdown">
-            <span class="countdown-icon">⏱️</span>
-            <span>响应倒计时: {{ formatCountdown(feedbackTimeoutCountdown) }}</span>
-          </div>
-          <div class="feedback-details">
-            <div class="detail-item">
-              <span class="detail-label">修改部位:</span>
-              <span class="detail-value">{{ feedback.problemLocation || '-' }}</span>
-            </div>
-            <div class="detail-item">
-              <span class="detail-label">期望效果:</span>
-              <span class="detail-value">{{ feedback.expectedEffect || '-' }}</span>
-            </div>
-            <div class="detail-item">
-              <span class="detail-label">参考案例:</span>
-              <span class="detail-value">{{ feedback.referenceCase || '-' }}</span>
+        
+        <div class="members-timeline-card card">
+          <h2>团队协作时间轴</h2>
+          <div class="timeline-entries">
+            <div *ngFor="let event of timelineEvents" class="timeline-entry">
+              <div class="timeline-dot"></div>
+              <div class="timeline-content">
+                <div class="timeline-header">
+                  <span class="timeline-author">{{ getEventAuthor(event.action) }}</span>
+                  <span class="timeline-time">{{ event.time }}</span>
+                </div>
+                <div class="timeline-text">{{ event.description }}</div>
+              </div>
             </div>
           </div>
         </div>
-        <div class="feedback-actions">
-          <button (click)="updateFeedbackStatus(feedback.id, '处理中')" [disabled]="feedback.status === '处理中' || feedback.status === '已解决'" class="secondary-btn">
-            标记为处理中
-          </button>
-          <button (click)="updateFeedbackStatus(feedback.id, '已解决')" [disabled]="feedback.status === '已解决'" class="primary-btn">
-            标记为已解决
-          </button>
-        </div>
       </div>
-    </div>
 
-    <!-- 设计师变更记录卡片 -->
-    <div class="designer-change-card card">
-      <h2>设计师变更记录</h2>
-      <div class="change-actions">
-        <button (click)="initiateDesignerChange('技能不匹配')" class="secondary-btn">发起变更 - 技能不匹配</button>
-        <button (click)="initiateDesignerChange('休假')" class="secondary-btn">发起变更 - 休假</button>
-      </div>
-      <div *ngIf="designerChanges.length === 0" class="empty-state">
-        <div class="empty-icon">👤</div>
-        <span>暂无设计师变更记录</span>
-      </div>
-      <div *ngFor="let change of designerChanges" class="change-item">
-        <div class="change-header">
-          <div class="change-time">{{ change.changeTime | date:'yyyy-MM-dd' }}</div>
-          <button *ngIf="!change.acceptanceTime" (click)="acceptDesignerChange(change.id)" class="accept-change-btn primary-btn">
-            确认承接
+      <!-- 项目文件标签页 -->
+      <div *ngIf="isActiveTab('files')" class="files-tab-content">
+        <div class="file-actions">
+          <button class="upload-btn primary-btn">
+            <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
+              <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
+              <polyline points="17 8 12 3 7 8"></polyline>
+              <line x1="12" y1="3" x2="12" y2="15"></line>
+            </svg>
+            <span>上传文件</span>
           </button>
+          <div class="file-filter">
+            <select class="file-filter-select">
+              <option value="all">全部文件</option>
+              <option value="images">图片</option>
+              <option value="documents">文档</option>
+              <option value="models">模型文件</option>
+            </select>
+          </div>
         </div>
-        <div class="change-details">
-          <div class="designer-change-info">
-            <div class="designer-change">
-              <span class="designer-label">原设计师:</span>
-              <span class="designer-name">{{ change.oldDesignerName }}</span>
+        
+        <div class="files-grid">
+          <div *ngFor="let file of projectFiles" class="file-card">
+            <div class="file-icon">
+              <svg *ngIf="file.type.includes('pdf')" width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="#EA4335" 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>
+                <polyline points="10 9 9 9 8 9"></polyline>
+              </svg>
+              <svg *ngIf="file.type.includes('jpg') || file.type.includes('png')" width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="#34A853" 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>
+              <svg *ngIf="file.type.includes('docx') || file.type.includes('doc')" width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="#4285F4" 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>
+                <polyline points="10 9 9 9 8 9"></polyline>
+              </svg>
+              <svg *ngIf="file.type.includes('rar') || file.type.includes('zip')" width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="#FBBC05" 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>
+                <polyline points="10 9 9 9 8 9"></polyline>
+              </svg>
+              <svg *ngIf="file.type.includes('max') || file.type.includes('obj')" width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="#9C27B0" 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>
+                <polyline points="10 9 9 9 8 9"></polyline>
+              </svg>
             </div>
-            <div class="designer-change">
-              <span class="designer-label">新设计师:</span>
-              <span class="designer-name">{{ change.newDesignerName }}</span>
+            <div class="file-info">
+              <div class="file-name">{{ file.name }}</div>
+              <div class="file-meta">
+                <span class="file-size">{{ file.size }}</span>
+                <span class="file-date">{{ file.date }}</span>
+              </div>
             </div>
+            <button class="file-action-btn">
+              <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
+                <circle cx="12" cy="12" r="1"></circle>
+                <circle cx="19" cy="12" r="1"></circle>
+                <circle cx="5" cy="12" r="1"></circle>
+              </svg>
+            </button>
           </div>
-          <div class="workload-info">
-            <span>已完成工作量: <strong>{{ change.completedWorkload }}%</strong></span>
+            <div class="file-icon">
+              <svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="#FBBC05" 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>
+                <polyline points="10 9 9 9 8 9"></polyline>
+              </svg>
+            </div>
+            <div class="file-info">
+              <div class="file-name">色彩方案.xlsx</div>
+              <div class="file-meta">
+                <span class="file-size">0.9MB</span>
+                <span class="file-date">2025-09-04</span>
+              </div>
+            </div>
+            <button class="file-action-btn">
+              <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
+                <circle cx="12" cy="12" r="1"></circle>
+                <circle cx="19" cy="12" r="1"></circle>
+                <circle cx="5" cy="12" r="1"></circle>
+              </svg>
+            </button>
           </div>
-          <div class="achievements">
-            <h4>历史阶段成果:</h4>
-            <ul>
-              <li *ngFor="let achievement of change.historicalAchievements">{{ achievement }}</li>
-            </ul>
+        </div>
+        
+        <div class="file-storage-info">
+          <div class="storage-bar">
+            <div class="storage-used" style="width: 45%"></div>
           </div>
-          <div *ngIf="change.acceptanceTime" class="change-status">
-            承接确认时间: {{ change.acceptanceTime | date:'yyyy-MM-dd' }}
+          <div class="storage-text">
+            <span>已使用 450MB / 1GB</span>
           </div>
         </div>
       </div>
     </div>
+  </div>
 
-    <!-- 分阶段结算记录卡片 -->
-    <div class="settlement-card card">
-      <h2>分阶段结算记录</h2>
-      <div *ngIf="settlements.length === 0" class="empty-state">
-        <div class="empty-icon">💰</div>
-        <span>暂无结算记录</span>
-      </div>
-      <div *ngIf="settlements.length > 0" class="settlement-table">
-        <table>
-          <thead>
-            <tr>
-              <th>阶段</th>
-              <th>比例</th>
-              <th>金额(元)</th>
-              <th>状态</th>
-              <th>完成时间</th>
-            </tr>
-          </thead>
-          <tbody>
-            <tr *ngFor="let settlement of settlements">
-              <td>{{ settlement.stage }}</td>
-              <td>{{ settlement.percentage }}%</td>
-              <td>{{ settlement.amount }}</td>
-              <td><span class="status-badge" [class.status-pending]="settlement.status === '待结算'" [class.status-settled]="settlement.status === '已结算'">{{ settlement.status }}</span></td>
-              <td>{{ settlement.completionTime | date:'yyyy-MM-dd' }}</td>
-            </tr>
-          </tbody>
-        </table>
-      </div>
+  <!-- 分阶段结算记录卡片 -->
+  <div class="settlement-card card">
+    <h2>分阶段结算记录</h2>
+    <div *ngIf="settlements.length === 0" class="empty-state">
+      <div class="empty-icon">💰</div>
+      <span>暂无结算记录</span>
+    </div>
+    <div *ngIf="settlements.length > 0" class="settlement-table">
+      <table>
+        <thead>
+          <tr>
+            <th>阶段</th>
+            <th>比例</th>
+            <th>金额(元)</th>
+            <th>状态</th>
+            <th>完成时间</th>
+          </tr>
+        </thead>
+        <tbody>
+          <tr *ngFor="let settlement of settlements">
+            <td>{{ settlement.stage }}</td>
+            <td>{{ settlement.percentage }}%</td>
+            <td>{{ settlement.amount }}</td>
+            <td><span class="status-badge" [class.status-pending]="settlement.status === '待结算'" [class.status-settled]="settlement.status === '已结算'">{{ settlement.status }}</span></td>
+            <td>{{ settlement.completionTime | date:'yyyy-MM-dd' }}</td>
+          </tr>
+        </tbody>
+      </table>
     </div>
   </div>
-</div>

+ 459 - 13
src/app/pages/designer/project-detail/project-detail.scss

@@ -1,9 +1,89 @@
+/* 项目详情页面样式文件 - 简化版本,直接针对HTML元素 */
 @use '../ios-theme.scss' as *;
 
-/* 项目详情页主样式 */
-.project-detail-container{padding:$ios-spacing-xl;background-color:$ios-background;color:$ios-text-primary;min-height:100vh}
-.card{background-color:$ios-background-secondary;border-radius:$ios-radius-lg;padding:$ios-spacing-lg;margin-bottom:$ios-spacing-xl}
-.project-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:$ios-spacing-xl;background-color:$ios-background-secondary;padding:$ios-spacing-lg;border-radius:$ios-radius-lg}
+/* 重置所有可能冲突的样式 */
+* {
+  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;
+}
+
+/* 项目详情容器 */
+.project-detail-container {
+  padding: 20px;
+  background-color: #f5f7fa;
+  color: #333;
+  min-height: 100vh;
+}
+
+/* 项目标题栏 */
+.project-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; /* 右侧列背景色 */
+}
+
+/* 确保响应式布局正常工作 */
+@media (max-width: 1024px) {
+  .progress-tab-content > .main-content-layout {
+    flex-direction: column !important;
+  }
+  
+  .progress-tab-content > .main-content-layout > .left-column,
+  .progress-tab-content > .main-content-layout > .right-column {
+    width: 100% !important;
+  }
+}
 .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}
@@ -31,8 +111,374 @@
 .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)}
 
-/* 主内容区网格布局 */
-.main-content-grid{display:grid;grid-template-columns:repeat(auto-fit, minmax(320px, 1fr));gap:$ios-spacing-xl}
+/* 顶部导航标签页样式 */
+.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}
@@ -336,8 +782,6 @@ h4{font-size:$ios-font-size-sm;font-weight:$ios-font-weight-medium;color:$ios-te
 .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}
@@ -345,8 +789,6 @@ h4{font-size:$ios-font-size-sm;font-weight:$ios-font-weight-medium;color:$ios-te
 .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}
@@ -366,8 +808,6 @@ h4{font-size:$ios-font-size-sm;font-weight:$ios-font-weight-medium;color:$ios-te
 .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}
@@ -399,7 +839,13 @@ h4{font-size:$ios-font-size-sm;font-weight:$ios-font-weight-medium;color:$ios-te
 /* 响应式设计 */
 @media (max-width: 768px){
 .project-detail-container{padding:$ios-spacing-md}
-.main-content-grid{grid-template-columns:1fr;gap:$ios-spacing-lg}
+.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}

+ 294 - 23
src/app/pages/designer/project-detail/project-detail.ts

@@ -22,13 +22,41 @@ interface ExceptionHistory {
   response?: string;
 }
 
+interface ProjectMember {
+  id: string;
+  name: string;
+  role: string;
+  avatar: string;
+  skillMatch: number;
+  progress: number;
+  contribution: number;
+}
+
+interface ProjectFile {
+  id: string;
+  name: string;
+  type: string;
+  size: string;
+  date: string;
+  url: string;
+}
+
+interface TimelineEvent {
+  id: string;
+  time: string;
+  title: string;
+  action: string;
+  description: string;
+}
+
 @Component({
   selector: 'app-project-detail',
   imports: [CommonModule, FormsModule],
   templateUrl: './project-detail.html',
-  styleUrl: './project-detail.scss'
+  styleUrls: ['./project-detail.scss', './debug-styles.scss']
 })
 export class ProjectDetail implements OnInit, OnDestroy {
+  // 项目基本数据
   projectId: string = '';
   project: Project | undefined;
   renderProgress: RenderProgress | undefined;
@@ -44,6 +72,7 @@ export class ProjectDetail implements OnInit, OnDestroy {
   private countdownInterval: any;
   projects: {id: string, name: string, status: string}[] = [];
   showDropdown: boolean = false;
+  currentStage: string = '';
   
   // 渲染异常反馈相关属性
   exceptionType: 'failed' | 'stuck' | 'quality' | 'other' = 'failed';
@@ -51,6 +80,26 @@ export class ProjectDetail implements OnInit, OnDestroy {
   exceptionScreenshotUrl: string | null = null;
   exceptionHistories: ExceptionHistory[] = [];
   isSubmittingFeedback: boolean = false;
+  selectedScreenshot: File | null = null;
+  screenshotPreview: string | null = null;
+  showExceptionForm: boolean = false;
+  
+  // 标签页相关
+  activeTab: 'progress' | 'members' | 'files' = 'progress';
+  tabs = [
+    { id: 'progress', name: '项目进度' },
+    { id: 'members', name: '项目成员' },
+    { id: 'files', name: '项目文件' }
+  ];
+
+  // 项目成员数据
+  projectMembers: ProjectMember[] = [];
+  
+  // 项目文件数据
+  projectFiles: ProjectFile[] = [];
+  
+  // 团队协作时间轴
+  timelineEvents: TimelineEvent[] = [];
 
   constructor(
     private route: ActivatedRoute,
@@ -58,10 +107,40 @@ export class ProjectDetail implements OnInit, OnDestroy {
     private router: Router
   ) {}
 
+  // 切换标签页
+  switchTab(tabId: 'progress' | 'members' | 'files') {
+    this.activeTab = tabId;
+  }
+
+  // 类型安全的标签页检查方法
+  isActiveTab(tabId: 'progress' | 'members' | 'files'): boolean {
+    return this.activeTab === tabId;
+  }
+
+  // 根据事件类型获取作者名称
+  getEventAuthor(action: string): string {
+    // 根据不同的action类型返回对应的作者名称
+    switch(action) {
+      case '完成':
+      case '更新':
+      case '优化':
+        return '李设计师';
+      case '收到':
+        return '李客服';
+      case '提交':
+        return '赵建模师';
+      default:
+        return '王组长';
+    }
+  }
+
   // 切换项目
   switchProject(projectId: string): void {
     this.projectId = projectId;
     this.loadProjectData();
+    this.loadProjectMembers();
+    this.loadProjectFiles();
+    this.loadTimelineEvents();
     // 更新URL但不触发组件重载
     this.router.navigate([], { relativeTo: this.route, queryParamsHandling: 'merge', queryParams: { id: projectId } });
   }
@@ -76,6 +155,9 @@ export class ProjectDetail implements OnInit, OnDestroy {
       this.projectId = params.get('id') || '';
       this.loadProjectData();
       this.loadExceptionHistories();
+      this.loadProjectMembers();
+      this.loadProjectFiles();
+      this.loadTimelineEvents();
     });
     
     // 添加点击事件监听器,当点击页面其他位置时关闭下拉菜单
@@ -120,6 +202,152 @@ export class ProjectDetail implements OnInit, OnDestroy {
     ];
   }
   
+  // 加载项目成员数据
+  loadProjectMembers(): void {
+    // 模拟API请求获取项目成员数据
+    setTimeout(() => {
+      this.projectMembers = [
+        {
+          id: '1',
+          name: '李设计师',
+          role: '主设计师',
+          avatar: '李',
+          skillMatch: 95,
+          progress: 65,
+          contribution: 75
+        },
+        {
+          id: '2',
+          name: '陈设计师',
+          role: '助理设计师',
+          avatar: '陈',
+          skillMatch: 88,
+          progress: 80,
+          contribution: 60
+        },
+        {
+          id: '3',
+          name: '王组长',
+          role: '项目组长',
+          avatar: '王',
+          skillMatch: 92,
+          progress: 70,
+          contribution: 70
+        },
+        {
+          id: '4',
+          name: '赵建模师',
+          role: '3D建模师',
+          avatar: '赵',
+          skillMatch: 90,
+          progress: 90,
+          contribution: 85
+        }
+      ];
+    }, 600);
+  }
+  
+  // 加载项目文件数据
+  loadProjectFiles(): void {
+    // 模拟API请求获取项目文件数据
+    setTimeout(() => {
+      this.projectFiles = [
+        {
+          id: '1',
+          name: '客厅设计方案V2.0.pdf',
+          type: 'pdf',
+          size: '2.5MB',
+          date: '2024-02-10',
+          url: '#'
+        },
+        {
+          id: '2',
+          name: '材质库集合.rar',
+          type: 'rar',
+          size: '45.8MB',
+          date: '2024-02-08',
+          url: '#'
+        },
+        {
+          id: '3',
+          name: '客厅渲染预览1.jpg',
+          type: 'jpg',
+          size: '3.2MB',
+          date: '2024-02-14',
+          url: '#'
+        },
+        {
+          id: '4',
+          name: '3D模型文件.max',
+          type: 'max',
+          size: '87.5MB',
+          date: '2024-02-12',
+          url: '#'
+        },
+        {
+          id: '5',
+          name: '客户需求文档.docx',
+          type: 'docx',
+          size: '1.2MB',
+          date: '2024-01-15',
+          url: '#'
+        },
+        {
+          id: '6',
+          name: '客厅渲染预览2.jpg',
+          type: 'jpg',
+          size: '3.8MB',
+          date: '2024-02-15',
+          url: '#'
+        }
+      ];
+    }, 700);
+  }
+  
+  // 加载团队协作时间轴数据
+  loadTimelineEvents(): void {
+    // 模拟API请求获取时间轴数据
+    setTimeout(() => {
+      this.timelineEvents = [
+        {
+          id: '1',
+          time: '2024-02-15 14:30',
+          title: '渲染完成',
+          action: '完成',
+          description: '客厅主视角渲染已完成,等待客户确认'
+        },
+        {
+          id: '2',
+          time: '2024-02-14 10:15',
+          title: '材质调整',
+          action: '更新',
+          description: '根据客户反馈调整了沙发和窗帘材质'
+        },
+        {
+          id: '3',
+          time: '2024-02-12 16:45',
+          title: '模型优化',
+          action: '优化',
+          description: '优化了模型面数,提高渲染效率'
+        },
+        {
+          id: '4',
+          time: '2024-02-10 09:30',
+          title: '客户反馈',
+          action: '收到',
+          description: '收到客户关于颜色和储物空间的反馈意见'
+        },
+        {
+          id: '5',
+          time: '2024-02-08 15:20',
+          title: '模型提交',
+          action: '提交',
+          description: '完成3D模型搭建并提交审核'
+        }
+      ];
+    }, 800);
+  }
+  
   // 加载历史反馈记录
   loadExceptionHistories(): void {
     this.projectService.getExceptionHistories(this.projectId).subscribe(histories => {
@@ -130,10 +358,39 @@ export class ProjectDetail implements OnInit, OnDestroy {
   loadProjectDetails(): void {
     this.projectService.getProjectById(this.projectId).subscribe(project => {
       this.project = project;
+      // 设置当前阶段
+      if (project) {
+        this.currentStage = project.currentStage || '';
+      }
       // 检查技能匹配度
       this.checkSkillMismatch();
     });
   }
+  
+  // 整理项目详情
+  organizeProject(): void {
+    // 模拟整理项目逻辑
+    alert('项目详情已整理');
+  }
+  
+  // 检查当前阶段是否显示特定卡片
+  shouldShowCard(cardType: string): boolean {
+    // 根据项目当前阶段决定是否显示特定卡片
+    switch (cardType) {
+      case 'modelCheck':
+        return ['建模阶段', '渲染阶段', '深化设计'].includes(this.currentStage);
+      case 'renderProgress':
+        return ['渲染阶段'].includes(this.currentStage);
+      case 'exceptionForm':
+        return ['渲染阶段', '后期处理'].includes(this.currentStage);
+      case 'designerChanges':
+        return true; // 所有阶段都显示
+      case 'settlement':
+        return true; // 所有阶段都显示
+      default:
+        return true;
+    }
+  }
 
   loadRenderProgress(): void {
     this.isLoadingRenderProgress = true;
@@ -372,33 +629,12 @@ export class ProjectDetail implements OnInit, OnDestroy {
     return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${remainingSeconds.toString().padStart(2, '0')}`;
   }
 
-  // 上传异常截图
-  uploadExceptionScreenshot(event: Event): void {
-    const input = event.target as HTMLInputElement;
-    if (input.files && input.files[0]) {
-      const file = input.files[0];
-      // 在实际应用中,这里应该上传文件到服务器
-      // 这里我们使用FileReader来生成一个预览URL
-      const reader = new FileReader();
-      reader.onload = (e) => {
-        this.exceptionScreenshotUrl = e.target?.result as string;
-      };
-      reader.readAsDataURL(file);
-    }
-  }
 
-  // 清除异常截图
-  clearExceptionScreenshot(): void {
-    this.exceptionScreenshotUrl = null;
-    const input = document.getElementById('screenshot-upload') as HTMLInputElement;
-    if (input) {
-      input.value = '';
-    }
-  }
 
   // 提交异常反馈
   submitExceptionFeedback(): void {
     if (!this.exceptionDescription.trim() || this.isSubmittingFeedback) {
+      alert('请填写异常类型和描述');
       return;
     }
 
@@ -423,6 +659,7 @@ export class ProjectDetail implements OnInit, OnDestroy {
       // 清空表单
       this.exceptionDescription = '';
       this.clearExceptionScreenshot();
+      this.showExceptionForm = false;
       
       // 显示成功消息
       alert('异常反馈已提交,技术支持将尽快处理');
@@ -431,6 +668,40 @@ export class ProjectDetail implements OnInit, OnDestroy {
     }, 1000);
   }
 
+  // 上传异常截图
+  uploadExceptionScreenshot(event: Event): void {
+    const input = event.target as HTMLInputElement;
+    if (input.files && input.files[0]) {
+      const file = input.files[0];
+      // 在实际应用中,这里应该上传文件到服务器
+      // 这里我们使用FileReader来生成一个预览URL
+      const reader = new FileReader();
+      reader.onload = (e) => {
+        this.exceptionScreenshotUrl = e.target?.result as string;
+      };
+      reader.readAsDataURL(file);
+    }
+  }
+
+  // 清除异常截图
+  clearExceptionScreenshot(): void {
+    this.exceptionScreenshotUrl = null;
+    const input = document.getElementById('screenshot-upload') as HTMLInputElement;
+    if (input) {
+      input.value = '';
+    }
+  }
+
+  // 联系组长
+  contactTeamLeader() {
+    alert(`已联系${this.project?.assigneeName || '项目组长'}`);
+  }
+
+  // 处理渲染超时预警
+  handleRenderTimeout() {
+    alert('已发送渲染超时预警通知');
+  }
+
   // 通知技术支持
   notifyTechnicalSupport(exception: ExceptionHistory): void {
     // 实际应用中应调用消息服务通知技术支持和客服

+ 3 - 52
src/app/pages/finance/reports/reports.html

@@ -77,7 +77,6 @@
             </option>
           </select>
         </div>
-      </div>
 
       <div class="params-actions">
         <button 
@@ -179,59 +178,12 @@
     <div class="chart-container">
       <div class="chart-header">
         <h3>数据可视化</h3>
-        <div class="chart-legend" *ngIf="filteredReportData.chartData">
-          <div class="legend-item" *ngFor="let data of filteredReportData.chartData">
-            <span class="legend-color" [style.backgroundColor]="data.color"></span>
-            <span class="legend-label">
-              {{ data.label }}: 
-              <span *ngIf="userRole() === 'teamLead' || data.value === 0; else hiddenValue">
-                {{ data.value > 0 ? formatAmount(data.value) : '--' }}
-              </span>
-              <ng-template #hiddenValue>
-                <span class="hidden-amount">--</span>
-              </ng-template>
-            </span>
-          </div>
-        </div>
       </div>
       
-      <!-- 柱状图展示 -->
-      <div class="chart-content" *ngIf="filteredReportData.chartType === 'bar' && filteredReportData.chartData">
-        <div class="bar-chart">
-          <div 
-            *ngFor="let data of filteredReportData.chartData" 
-            class="bar-item" 
-            [style.width.%]="(data.value / maxValue(filteredReportData.chartData) * 100)"
-            [style.backgroundColor]="data.color"
-            title="{{ data.label }}: {{ userRole() === 'teamLead' && data.value > 0 ? formatAmount(data.value) : '--' }}"
-          >
-            <div class="bar-label">{{ data.label }}</div>
-            <div class="bar-value" *ngIf="userRole() === 'teamLead' || data.value === 0; else hiddenBarValue">
-              {{ data.value > 0 ? formatAmount(data.value) : '--' }}
-            </div>
-            <ng-template #hiddenBarValue>
-              <div class="bar-value hidden-amount">--</div>
-            </ng-template>
-          </div>
-        </div>
-      </div>
-      
-      <!-- 饼图展示 -->
-      <div class="chart-content" *ngIf="filteredReportData.chartType === 'pie' && filteredReportData.chartData">
-        <div class="pie-chart">
-          <!-- 简化的饼图实现 -->
-          <div class="pie-container">
-            <div 
-              *ngFor="let data of filteredReportData.chartData; let i = index" 
-              class="pie-slice" 
-              [style.backgroundColor]="data.color"
-              [style.transform]="getPieTransform(i, filteredReportData.chartData)"
-            ></div>
-          </div>
-        </div>
-      </div>
+      <!-- ECharts 图表容器 -->
+      <div id="echarts-container" #chartContainer style="width: 100%; height: 400px;"></div>
       
-      <!-- 表格展示 -->
+      <!-- 表格展示(仅在选择表格类型时显示) -->
       <div class="chart-content table-container" *ngIf="filteredReportData.chartType === 'table' && filteredReportData.chartData">
         <table class="data-table">
           <thead>
@@ -300,4 +252,3 @@
       <p>您当前以初级组员身份查看报表,部分敏感数据已隐藏。如需查看完整数据,请联系管理员提升权限。</p>
     </div>
   </div>
-</div>

+ 260 - 5
src/app/pages/finance/reports/reports.ts

@@ -1,9 +1,9 @@
-import { Component, OnInit } from '@angular/core';
 import { CommonModule } from '@angular/common';
 import { FormsModule } from '@angular/forms';
 import { RouterModule } from '@angular/router';
-import { signal } from '@angular/core';
+import { Component, signal, ElementRef, ViewChild, OnInit, AfterViewInit, effect } from '@angular/core';
 import { AuthService } from '../../../services/auth.service';
+// 使用全局echarts变量,不导入模块
 
 // 报表类型定义
 export type ReportType = 
@@ -52,9 +52,14 @@ export interface ReportData {
   selector: 'app-reports',
   imports: [CommonModule, FormsModule, RouterModule],
   templateUrl: './reports.html',
-  styleUrl: './reports.scss'
+  styleUrl: './reports.scss',
+  standalone: true
 })
-export class Reports implements OnInit {
+export class Reports implements OnInit, AfterViewInit {
+  @ViewChild('chartContainer') chartContainer!: ElementRef;
+  private chartInstance: any | null = null;
+  private chartInitialized = false;
+  
   // 报表类型选项
   reportTypes: { value: ReportType; label: string }[] = [
     { value: 'financial_summary', label: '财务汇总报表' },
@@ -123,7 +128,17 @@ export class Reports implements OnInit {
   // 历史报表记录
   historicalReports = signal<ReportData[]>([]);
 
-  constructor(private authService: AuthService) {}
+  constructor(private authService: AuthService) {
+    // 创建effect监听报表数据变化
+    effect(() => {
+      // 访问filteredReportData以确保它被计算
+      this.filteredReportData;
+      
+      if (this.filteredReportData && this.chartInitialized) {
+        this.updateChart();
+      }
+    });
+  }
 
   ngOnInit(): void {
     // 初始化用户角色
@@ -131,6 +146,246 @@ export class Reports implements OnInit {
     // 加载历史报表记录
     this.loadHistoricalReports();
   }
+
+  // 视图初始化后创建图表
+  ngAfterViewInit(): void {
+    this.initChart();
+  }
+
+  // 初始化echarts图表
+  private initChart(): void {
+    if (this.chartContainer && this.chartContainer.nativeElement) {
+      this.chartInstance = echarts.init(this.chartContainer.nativeElement);
+      this.chartInitialized = true;
+      
+      // 设置响应式
+      window.addEventListener('resize', () => {
+        this.chartInstance?.resize();
+      });
+    }
+  }
+
+  // 更新图表数据和配置
+  private updateChart(): void {
+    if (!this.chartInitialized || !this.chartInstance || !this.filteredReportData) {
+      return;
+    }
+
+    const report = this.filteredReportData;
+    const option = this.generateChartOption(report);
+    
+    this.chartInstance.setOption(option);
+  }
+
+  // 生成图表配置
+  private generateChartOption(report: ReportData): any {
+    const isTeamLead = this.userRole() === 'teamLead';
+    const chartData = isTeamLead ? report.chartData : report.chartData.map(item => ({...item, value: 0}));
+    
+    // 根据图表类型生成不同的配置
+    switch (report.chartType) {
+      case 'bar':
+        return this.generateBarChartOption(chartData, isTeamLead);
+      case 'line':
+        return this.generateLineChartOption(chartData, isTeamLead);
+      case 'pie':
+        return this.generatePieChartOption(chartData, isTeamLead);
+      default:
+        return {};
+    }
+  }
+
+  // 生成柱状图配置
+  private generateBarChartOption(data: ChartDataPoint[], isTeamLead: boolean): any {
+    const labels = data.map(item => item.label);
+    const values = isTeamLead ? data.map(item => item.value) : data.map(() => 0);
+    const colors = data.map(item => item.color || '#3498db');
+    
+    return {
+      title: {
+        text: '柱状图数据',
+        left: 'center',
+        textStyle: {
+          fontSize: 16
+        }
+      },
+      tooltip: {
+        trigger: 'axis',
+        axisPointer: {
+          type: 'shadow'
+        },
+        formatter: (params: any) => {
+          const data = params[0];
+          const label = data.name;
+          const value = isTeamLead ? this.formatAmount(data.value) : '--';
+          return `${label}: ${value}`;
+        }
+      },
+      xAxis: {
+        type: 'category',
+        data: labels,
+        axisLabel: {
+          interval: 0,
+          rotate: 30
+        }
+      },
+      yAxis: {
+        type: 'value',
+        axisLabel: {
+          formatter: (value: number) => {
+            return isTeamLead ? this.formatAmount(value) : '--';
+          }
+        }
+      },
+      series: [{
+        data: values,
+        type: 'bar',
+        itemStyle: {
+          color: (params: any) => colors[params.dataIndex]
+        },
+        label: {
+          show: true,
+          position: 'top',
+          formatter: (params: any) => {
+            return isTeamLead && params.value > 0 ? this.formatAmount(params.value) : '';
+          }
+        }
+      }]
+    };
+  }
+
+  // 生成折线图配置
+  private generateLineChartOption(data: ChartDataPoint[], isTeamLead: boolean): any {
+    const labels = data.map(item => item.label);
+    const values = isTeamLead ? data.map(item => item.value) : data.map(() => 0);
+    const colors = data.map(item => item.color || '#3498db');
+    
+    return {
+      title: {
+        text: '折线图数据',
+        left: 'center',
+        textStyle: {
+          fontSize: 16
+        }
+      },
+      tooltip: {
+        trigger: 'axis',
+        formatter: (params: any) => {
+          const data = params[0];
+          const label = data.name;
+          const value = isTeamLead ? this.formatAmount(data.value) : '--';
+          return `${label}: ${value}`;
+        }
+      },
+      xAxis: {
+        type: 'category',
+        data: labels
+      },
+      yAxis: {
+        type: 'value',
+        axisLabel: {
+          formatter: (value: number) => {
+            return isTeamLead ? this.formatAmount(value) : '--';
+          }
+        }
+      },
+      series: [{
+        data: values,
+        type: 'line',
+        smooth: true,
+        symbol: 'circle',
+        symbolSize: 8,
+        itemStyle: {
+          color: colors[0]
+        },
+        lineStyle: {
+          width: 3
+        },
+        areaStyle: {
+          color: {
+            type: 'linear',
+            x: 0,
+            y: 0,
+            x2: 0,
+            y2: 1,
+            colorStops: [{
+              offset: 0,
+              color: colors[0] + '80'
+            }, {
+              offset: 1,
+              color: colors[0] + '10'
+            }]
+          }
+        },
+        label: {
+          show: true,
+          position: 'top',
+          formatter: (params: any) => {
+            return isTeamLead && params.value > 0 ? this.formatAmount(params.value) : '';
+          }
+        }
+      }]
+    };
+  }
+
+  // 生成饼图配置
+  private generatePieChartOption(data: ChartDataPoint[], isTeamLead: boolean): any {
+    const pieData = isTeamLead ? 
+      data.map(item => ({ name: item.label, value: item.value })) : 
+      data.map(item => ({ name: item.label, value: 1 }));
+    
+    const colors = data.map(item => item.color || '#3498db');
+    
+    return {
+      title: {
+        text: '饼图数据',
+        left: 'center',
+        textStyle: {
+          fontSize: 16
+        }
+      },
+      tooltip: {
+        trigger: 'item',
+        formatter: (params: any) => {
+          const value = isTeamLead ? this.formatAmount(params.value) : '--';
+          const percent = isTeamLead ? `${params.percent}%` : '--';
+          return `${params.name}: ${value} (${percent})`;
+        }
+      },
+      legend: {
+        orient: 'vertical',
+        left: 'left',
+        formatter: (name: string) => {
+          const item = data.find(d => d.label === name);
+          if (!item) return name;
+          const value = isTeamLead && item.value > 0 ? this.formatAmount(item.value) : '--';
+          return `${name}: ${value}`;
+        }
+      },
+      series: [{
+        name: '数据',
+        type: 'pie',
+        radius: '60%',
+        center: ['60%', '50%'],
+        data: pieData,
+        emphasis: {
+          itemStyle: {
+            shadowBlur: 10,
+            shadowOffsetX: 0,
+            shadowColor: 'rgba(0, 0, 0, 0.5)'
+          }
+        },
+        itemStyle: {
+          color: (params: any) => colors[params.dataIndex]
+        },
+        label: {
+          formatter: (params: any) => {
+            return isTeamLead ? `${params.name}\n${this.formatAmount(params.value)}\n${params.percent}%` : params.name;
+          }
+        }
+      }]
+    };
+  }
   
   // 初始化用户角色
   initializeUserRole(): void {

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

@@ -1,7 +1,7 @@
 // 质量管理页面样式
 
-// 导入iOS主题变量
-@import '../ios-theme';
+// 导入iOS主题变量 - 使用新的Sass @use语法以避免弃用警告
+@use '../ios-theme' as *;
 
 // 全局样式重置
 .quality-management-main {

+ 2 - 59
src/app/shared/components/designer-nav/designer-nav.html

@@ -1,13 +1,6 @@
 <!-- 设计师顶部导航栏 -->
 <header class="top-navbar">
   <div class="navbar-left">
-    <button class="menu-toggle" (click)="toggleSidebar()">
-      <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor">
-        <line x1="3" y1="12" x2="21" y2="12"></line>
-        <line x1="3" y1="6" x2="21" y2="6"></line>
-        <line x1="3" y1="18" x2="21" y2="18"></line>
-      </svg>
-    </button>
     <h1 class="app-title">设计师工作台</h1>
   </div>
   
@@ -44,58 +37,8 @@
 
 <!-- 主要内容区 -->
 <main class="main-content">
-  <!-- 左侧侧边栏 -->
-  <aside class="sidebar" [class.collapsed]="!sidebarOpen">
-    <nav class="sidebar-nav">
-      <a routerLink="/designer/dashboard" class="nav-item" routerLinkActive="active">
-        <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor">
-          <path d="M3 13h8V3H3v10zm0 8h8v-6H3v6zm10 0h8V11h-8v10zm0-18v6h8V3h-8z"></path>
-        </svg>
-        <span>工作台</span>
-      </a>
-      <a routerLink="/designer/personal-board" class="nav-item" routerLinkActive="active">
-        <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>
-        <span>个人看板</span>
-      </a>
-      <a routerLink="/designer/project-detail/1" class="nav-item" routerLinkActive="active">
-        <svg width="20" height="20" 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>
-      </a>
-      <a routerLink="/designer/case-library" class="nav-item" routerLinkActive="active">
-        <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>
-        <span>案例库</span>
-      </a>
-    </nav>
-    
-    <div class="sidebar-footer">
-      <div class="storage-info">
-        <span>在线时长: {{onlineHours}}h</span>
-      </div>
-      <button class="logout-btn">
-        <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
-          <path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"></path>
-          <polyline points="16 17 21 12 16 7"></polyline>
-          <line x1="21" y1="12" x2="9" y2="12"></line>
-        </svg>
-        <span>退出登录</span>
-      </button>
-    </div>
-  </aside>
-
-  <!-- 中间内容区 -->
-  <div class="content-wrapper" [class.expanded]="!sidebarOpen">
+  <!-- 中间内容区 - 侧边栏已删除 -->
+  <div class="content-wrapper">
     <ng-content></ng-content>
   </div>
 </main>

+ 18 - 6
src/app/shared/components/designer-nav/designer-nav.scss

@@ -164,12 +164,24 @@ $transition: all 0.3s ease;
 
 // 左侧侧边栏
 .sidebar {
-  width: 220px;
-  background-color: $background-primary;
-  border-right: 1px solid $border-color;
-  display: flex;
-  flex-direction: column;
-  transition: $transition;
+  display: none;
+  width: 0;
+  visibility: hidden;
+  position: absolute;
+  left: -9999px;
+
+  // 如果需要在某些页面显示侧边栏,可以通过其他类来覆盖
+  .show-sidebar & {
+    display: flex;
+    width: 220px;
+    background-color: $background-primary;
+    border-right: 1px solid $border-color;
+    visibility: visible;
+    position: relative;
+    left: 0;
+    flex-direction: column;
+    transition: $transition;
+  }
 
   .sidebar-nav {
     flex: 1;

+ 1 - 7
src/app/shared/components/designer-nav/designer-nav.ts

@@ -1,12 +1,11 @@
 import { Component, Input } from '@angular/core';
 import { CommonModule } from '@angular/common';
 import { FormsModule } from '@angular/forms';
-import { RouterLink, RouterLinkActive } from '@angular/router';
 
 @Component({
   selector: 'app-designer-nav',
   standalone: true,
-  imports: [CommonModule, FormsModule, RouterLink, RouterLinkActive],
+  imports: [CommonModule, FormsModule],
   templateUrl: './designer-nav.html',
   styleUrl: './designer-nav.scss'
 })
@@ -14,11 +13,6 @@ export class DesignerNavComponent {
   @Input() userName: string = '设计师用户';
   @Input() onlineHours: number = 4.8;
   
-  sidebarOpen = true;
   searchTerm = '';
   currentDate = new Date();
-
-  toggleSidebar() {
-    this.sidebarOpen = !this.sidebarOpen;
-  }
 }

+ 39 - 0
src/app/shared/styles/_variables.scss

@@ -0,0 +1,39 @@
+// 共享样式变量文件
+// 此文件包含应用程序中常用的样式变量,用于保持设计一致性并减少重复代码
+
+// 颜色变量\$primary-color: #165DFF;        // 主色调 - 蓝色
+$secondary-color: #6B7280;      // 次要颜色 - 灰色
+$success-color: #00B42A;        // 成功颜色 - 绿色
+$warning-color: #FFAA00;        // 警告颜色 - 黄色
+$error-color: #F53F3F;          // 错误颜色 - 红色
+$text-primary: #333333;         // 主要文本颜色
+$text-secondary: #666666;       // 次要文本颜色
+$text-tertiary: #999999;        // 三级文本颜色
+$border-color: #E5E7EB;         // 边框颜色
+$bg-light: #F9FAFB;             // 浅色背景
+$bg-white: #FFFFFF;             // 白色背景
+
+// 阴影变量
+$shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
+$shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
+$shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
+
+// 过渡动画
+$transition-fast: 0.2s ease;
+$transition-normal: 0.3s ease;
+$transition-slow: 0.5s ease;
+
+// 圆角
+$border-radius-sm: 6px;
+$border-radius-md: 8px;
+$border-radius-lg: 12px;
+$border-radius-xl: 16px;
+$border-radius-full: 9999px;
+
+// 间距
+$spacing-xs: 4px;
+$spacing-sm: 8px;
+$spacing-md: 16px;
+$spacing-lg: 24px;
+$spacing-xl: 32px;
+$spacing-xxl: 48px;

+ 28 - 0
src/app/typings.d.ts

@@ -0,0 +1,28 @@
+// 全局echarts变量声明
+declare global {
+  interface Window {
+    echarts: any;
+  }
+}
+
+declare const echarts: any;
+
+declare namespace echarts {
+  interface EChartsOption {
+    [key: string]: any;
+  }
+  
+  interface ECharts {
+    setOption: (option: EChartsOption, notMerge?: boolean, lazyUpdate?: boolean) => void;
+    resize: (options?: any) => void;
+    dispatchAction: (payload: any) => void;
+    showLoading: (type?: string, opts?: any) => void;
+    hideLoading: () => void;
+    clear: () => void;
+    dispose: () => void;
+  }
+  
+  function init(dom: HTMLElement, theme?: string, options?: any): ECharts;
+  function registerTheme(name: string, theme: any): void;
+  function registerMap(mapName: string, geoJSON: any, specialAreas?: any): void;
+}

+ 16 - 8
src/index.html

@@ -1,14 +1,22 @@
 <!doctype html>
 <html lang="en">
 <head>
-  <meta charset="utf-8">
-  <title>YssProject</title>
-  <base href="/">
-  <meta name="viewport" content="width=device-width, initial-scale=1">
-  <link rel="icon" type="image/x-icon" href="favicon.ico">
-  <link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500&display=swap" rel="stylesheet">
-  <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
-</head>
+    <meta charset="utf-8">
+    <title>YssProject</title>
+    <base href="/">
+    <meta name="viewport" content="width=device-width, initial-scale=1">
+    <link rel="icon" type="image/x-icon" href="favicon.ico">
+    <link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500&display=swap" rel="stylesheet">
+    <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
+    
+    <!-- 使用国内CDN引入echarts -->
+    <script src="https://unpkg.com/echarts@6.0.0/dist/echarts.min.js"></script>
+    <!-- 声明全局变量告知Angular使用外部的echarts -->
+    <script>
+      // 全局变量,供Angular应用使用
+      window.echarts = echarts;
+    </script>
+  </head>
 <body>
   <app-root></app-root>
 </body>

+ 1 - 1
tsconfig.app.json

@@ -4,7 +4,7 @@
   "extends": "./tsconfig.json",
   "compilerOptions": {
     "outDir": "./out-tsc/app",
-    "types": []
+    "types": ["echarts"]
   },
   "include": [
     "src/**/*.ts"