Prechádzať zdrojové kódy

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

徐福静0235668 1 deň pred
rodič
commit
9225b4e19e

+ 614 - 156
src/app/pages/designer/dashboard/dashboard.scss

@@ -3,6 +3,12 @@
 // 导入iOS主题变量
 @use '../ios-theme.scss' as *;
 
+// 导入响应式布局样式
+@import './responsive-layout.scss';
+
+// 导入交互效果样式
+@import './interactive-effects.scss';
+
 .dashboard-container {
   max-width: 1200px;
   margin: 0 auto;
@@ -12,166 +18,369 @@
 }
 
 .dashboard-header {
-  margin-bottom: 30px;
+  margin-bottom: 32px;
+  padding: 24px 0;
+  border-bottom: 1px solid rgba(0, 0, 0, 0.06);
+  position: relative;
+
+  // 添加装饰性渐变线
+  &::after {
+    content: '';
+    position: absolute;
+    bottom: -1px;
+    left: 0;
+    right: 0;
+    height: 2px;
+    background: linear-gradient(90deg, #007aff 0%, #5ac8fa 50%, transparent 100%);
+    opacity: 0.6;
+  }
+
   h1 {
-    font-size: 32px;
-    color: $ios-text-primary;
-    font-weight: $ios-font-weight-semibold;
+    font-size: 36px;
+    color: #1a202c;
+    font-weight: 700;
     font-family: $ios-font-family;
-    margin-bottom: 20px;
+    margin-bottom: 8px;
+    letter-spacing: -0.8px;
+    line-height: 1.2;
+    background: linear-gradient(135deg, #1a202c 0%, #2d3748 100%);
+    -webkit-background-clip: text;
+    -webkit-text-fill-color: transparent;
+    background-clip: text;
+    position: relative;
+
+    // 添加微妙的文字阴影效果
+    &::before {
+      content: '';
+      position: absolute;
+      top: 0;
+      left: 0;
+      right: 0;
+      bottom: 0;
+      background: linear-gradient(135deg, rgba(0, 122, 255, 0.1) 0%, rgba(90, 200, 250, 0.05) 100%);
+      z-index: -1;
+      border-radius: 8px;
+      transform: scale(1.05);
+      opacity: 0;
+      transition: opacity 0.3s ease;
+    }
+
+    &:hover::before {
+      opacity: 1;
+    }
+  }
+
+  .subtitle {
+    color: #64748b;
+    font-size: 16px;
+    font-weight: 500;
+    margin-top: 8px;
+    letter-spacing: 0.2px;
   }
 }
 
-/* 顶部导航样式 - 重写为白色背景蓝色过渡效果 */
+/* 顶部导航样式 - 现代化升级 */
 .dashboard-nav {
   display: flex;
-  background-color: #ffffff; /* 设置底色为白色 */
-  border-radius: 12px; /* 增大圆角 */
-  padding: 4px; /* 保留内边距 */
-  margin-bottom: 20px;
-  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05); /* 添加轻微阴影 */
-  border: 1px solid #f0f0f0; /* 添加细微边框 */
+  background: linear-gradient(135deg, #ffffff 0%, #fafbfc 100%);
+  border-radius: 16px;
+  padding: 6px;
+  margin-bottom: 24px;
+  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08), 0 1px 3px rgba(0, 0, 0, 0.05);
+  border: 1px solid rgba(0, 0, 0, 0.06);
+  position: relative;
+  overflow: hidden;
+
+  // 添加背景光晕效果
+  &::before {
+    content: '';
+    position: absolute;
+    top: 0;
+    left: 0;
+    right: 0;
+    bottom: 0;
+    background: linear-gradient(135deg, rgba(255, 255, 255, 0.8) 0%, rgba(255, 255, 255, 0.4) 100%);
+    pointer-events: none;
+  }
   
   .nav-btn {
     flex: 1;
-    padding: 14px 24px; /* 增加垂直内边距 */
+    padding: 16px 24px;
     border: none;
-    border-radius: 10px; /* 增大圆角 */
+    border-radius: 12px;
     background-color: transparent;
-    color: #666666; /* 默认文字颜色 */
-    font-size: 16px; /* 略微增大字体 */
-    font-weight: 500;
+    color: #64748b;
+    font-size: 16px;
+    font-weight: 600;
     cursor: pointer;
     transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
     font-family: $ios-font-family;
     position: relative;
     overflow: hidden;
-    z-index: 1;
+    z-index: 2;
     text-align: center;
+    letter-spacing: 0.3px;
     
-    // 按钮点击效果
-    &:active {
-      transform: scale(0.97);
+    // 悬停效果
+    &::before {
+      content: '';
+      position: absolute;
+      top: 0;
+      left: -100%;
+      width: 100%;
+      height: 100%;
+      background: linear-gradient(90deg, transparent, rgba(0, 122, 255, 0.1), transparent);
+      transition: left 0.4s;
     }
-    
-    // 活动状态样式
-    &.active {
-      color: #ffffff; /* 活动状态文字为白色 */
-      font-weight: 600;
+
+    &:hover:not(.active) {
+      color: #374151;
+      background: rgba(255, 255, 255, 0.8);
+      transform: translateY(-1px);
+      box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
       
-      // 蓝色背景动画效果
       &::before {
+        left: 100%;
+      }
+    }
+
+    &.active {
+      background: linear-gradient(135deg, #007aff 0%, #5ac8fa 100%);
+      color: white;
+      transform: translateY(-2px);
+      box-shadow: 0 6px 20px rgba(0, 122, 255, 0.3), 0 2px 4px rgba(0, 122, 255, 0.2);
+      font-weight: 700;
+
+      // 活跃状态的光晕效果
+      &::after {
         content: '';
         position: absolute;
-        top: 0;
-        left: 0;
-        right: 0;
-        bottom: 0;
-        background-color: #007aff; /* 设置为iOS蓝色 */
-        border-radius: 10px;
-        box-shadow: 0 4px 12px rgba(0, 122, 255, 0.3); /* 蓝色阴影效果 */
+        top: -2px;
+        left: -2px;
+        right: -2px;
+        bottom: -2px;
+        background: linear-gradient(135deg, #007aff 0%, #5ac8fa 100%);
+        border-radius: 14px;
         z-index: -1;
-        animation: buttonSlideIn 0.3s cubic-bezier(0.4, 0, 0.2, 1) forwards;
+        opacity: 0.3;
+        filter: blur(6px);
       }
     }
-    
-    // 悬停效果
-    &:hover:not(.active) {
-      background-color: rgba(0, 122, 255, 0.05); /* 轻微蓝色背景 */
-      color: #007aff; /* 文字变为蓝色 */
-    }
-  }
-}
 
-// 按钮切换动画 - 优化效果
-@keyframes buttonSlideIn {
-  0% {
-    transform: translateX(100%);
-    opacity: 0;
-  }
-  100% {
-    transform: translateX(0);
-    opacity: 1;
+    &:active {
+      transform: translateY(-1px);
+    }
   }
 }
-
+    
 .dashboard-main {
   display: grid;
   grid-template-columns: 1fr;
-  gap: 24px;
+  gap: 32px;
+  padding: 0 4px; // 添加轻微的水平内边距
 }
 
-/* 视图切换按钮样式 */
+/* 视图切换按钮样式 - 现代化升级 */
 .view-toggle {
   display: flex;
   justify-content: flex-end;
-  margin-bottom: 8px;
+  margin-bottom: 16px;
+  gap: 8px;
 
   .toggle-btn {
-    background: $ios-card-background;
-    color: $ios-text-primary;
-    border: 1px solid $ios-border;
-    border-radius: $ios-radius-md;
-    padding: 8px 14px;
+    background: linear-gradient(135deg, #ffffff 0%, #f8fafc 100%);
+    color: #64748b;
+    border: 1px solid rgba(0, 0, 0, 0.08);
+    border-radius: 12px;
+    padding: 12px 20px;
     cursor: pointer;
     font-family: $ios-font-family;
-    transition: all .2s ease;
+    font-weight: 600;
+    font-size: 14px;
+    transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
+    position: relative;
+    overflow: hidden;
+    display: flex;
+    align-items: center;
+    gap: 8px;
+    letter-spacing: 0.3px;
+    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
 
-    &:hover {
-      background-color: color-mix(in srgb, $ios-background-secondary 70%, white);
+    // 图标样式
+    ion-icon {
+      font-size: 18px;
+      transition: all 0.3s ease;
+    }
+
+    // 悬停效果
+    &::before {
+      content: '';
+      position: absolute;
+      top: 0;
+      left: -100%;
+      width: 100%;
+      height: 100%;
+      background: linear-gradient(90deg, transparent, rgba(0, 122, 255, 0.1), transparent);
+      transition: left 0.4s;
+    }
+
+    &:hover:not(.active) {
+      color: #374151;
+      background: linear-gradient(135deg, #f1f5f9 0%, #e2e8f0 100%);
+      transform: translateY(-2px);
+      box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
+      
+      &::before {
+        left: 100%;
+      }
+      
+      ion-icon {
+        transform: scale(1.1);
+      }
+    }
+
+    &.active {
+      background: linear-gradient(135deg, #007aff 0%, #5ac8fa 100%);
+      color: white;
+      border-color: transparent;
+      box-shadow: 0 4px 16px rgba(0, 122, 255, 0.3), 0 2px 4px rgba(0, 122, 255, 0.2);
+      transform: translateY(-2px);
+      
+      ion-icon {
+        transform: scale(1.1);
+        filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.2));
+      }
+
+      // 活跃状态的光晕效果
+      &::after {
+        content: '';
+        position: absolute;
+        top: -2px;
+        left: -2px;
+        right: -2px;
+        bottom: -2px;
+        background: linear-gradient(135deg, #007aff 0%, #5ac8fa 100%);
+        border-radius: 14px;
+        z-index: -1;
+        opacity: 0.3;
+        filter: blur(6px);
+      }
+    }
+
+    &:active {
+      transform: translateY(-1px);
     }
   }
 }
 
-/* 列表视图样式 */
+/* 列表视图样式 - 现代化升级 */
 .list-section {
-  background: $ios-card-background;
-  border-radius: $ios-radius-lg;
-  border: 1px solid $ios-border;
-  box-shadow: $ios-shadow-card;
+  background: linear-gradient(135deg, #ffffff 0%, #fafbfc 100%);
+  border-radius: 16px;
+  border: 1px solid rgba(0, 0, 0, 0.06);
+  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08), 0 1px 3px rgba(0, 0, 0, 0.05);
   overflow: hidden;
+  backdrop-filter: blur(10px);
 
   .list-header {
     display: grid;
     grid-template-columns: 120px 1.2fr 1.2fr 120px 180px 120px 160px;
-    padding: 12px 16px;
-    background: $ios-background-secondary;
-    color: $ios-text-secondary;
-    font-weight: $ios-font-weight-medium;
-    border-bottom: 1px solid $ios-border;
+    padding: 16px 20px;
+    background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);
+    color: #64748b;
+    font-weight: 600;
+    font-size: 13px;
+    letter-spacing: 0.5px;
+    text-transform: uppercase;
+    border-bottom: 1px solid rgba(0, 0, 0, 0.08);
+    position: sticky;
+    top: 0;
+    z-index: 10;
   }
 
   .list-body {
     .list-row {
       display: grid;
       grid-template-columns: 120px 1.2fr 1.2fr 120px 180px 120px 160px;
-      padding: 12px 16px;
+      padding: 16px 20px;
       align-items: center;
-      border-bottom: 1px solid $ios-border;
-      transition: background .2s ease;
+      border-bottom: 1px solid rgba(0, 0, 0, 0.04);
+      transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
+      position: relative;
+      
+      // 添加微妙的悬停效果
+      &::before {
+        content: '';
+        position: absolute;
+        left: 0;
+        top: 0;
+        bottom: 0;
+        width: 4px;
+        background: linear-gradient(135deg, #007aff 0%, #5ac8fa 100%);
+        transform: scaleY(0);
+        transition: transform 0.3s ease;
+      }
 
       &:hover {
-        background: rgba(0,0,0,0.02);
+        background: linear-gradient(135deg, rgba(0, 122, 255, 0.02) 0%, rgba(90, 200, 250, 0.01) 100%);
+        transform: translateX(4px);
+        box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
+        
+        &::before {
+          transform: scaleY(1);
+        }
+      }
+      
+      &:last-child {
+        border-bottom: none;
       }
 
       .col {
         font-size: 14px;
-        color: $ios-text-primary;
+        color: #374151;
+        font-weight: 500;
       }
 
       .actions-col {
         display: flex;
-        gap: 10px;
+        gap: 12px;
+        
         .btn-link {
           background: transparent;
           border: none;
           color: #007aff;
           cursor: pointer;
-          padding: 6px 8px;
-          border-radius: $ios-radius-sm;
+          padding: 8px 12px;
+          border-radius: 8px;
+          font-weight: 600;
+          font-size: 13px;
+          transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
+          position: relative;
+          overflow: hidden;
+          
+          &::before {
+            content: '';
+            position: absolute;
+            top: 0;
+            left: -100%;
+            width: 100%;
+            height: 100%;
+            background: linear-gradient(90deg, transparent, rgba(0, 122, 255, 0.1), transparent);
+            transition: left 0.4s;
+          }
 
           &:hover {
-            background: rgba(0,122,255,0.08);
+            background: rgba(0, 122, 255, 0.08);
+            transform: translateY(-1px);
+            box-shadow: 0 2px 8px rgba(0, 122, 255, 0.2);
+            
+            &::before {
+              left: 100%;
+            }
+          }
+          
+          &:active {
+            transform: translateY(0);
           }
         }
       }
@@ -179,192 +388,441 @@
       .urgency-col {
         display: flex;
         align-items: center;
-        gap: 8px;
+        gap: 10px;
+        
         .urgency-dot {
-          width: 10px;
-          height: 10px;
+          width: 12px;
+          height: 12px;
           border-radius: 50%;
           display: inline-block;
+          box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
+          position: relative;
+          
+          // 添加脉冲动画效果
+          &::after {
+            content: '';
+            position: absolute;
+            top: -2px;
+            left: -2px;
+            right: -2px;
+            bottom: -2px;
+            border-radius: 50%;
+            background: inherit;
+            opacity: 0.3;
+            animation: pulse 2s infinite;
+          }
         }
+        
         .urgency-text {
-          font-weight: $ios-font-weight-medium;
+          font-weight: 600;
+          font-size: 13px;
+          letter-spacing: 0.3px;
         }
       }
 
-      /* 紧急度颜色 */
-      .urgency-overdue { background: $ios-danger; }
-      .urgency-high { background: #ff9500; }
-      .urgency-medium { background: #ffcc00; }
-      .urgency-low { background: #34c759; }
+      /* 紧急度颜色 - 现代化升级 */
+      .urgency-overdue { 
+        background: linear-gradient(135deg, #ef4444 0%, #f87171 100%);
+        
+        .urgency-text {
+          color: #dc2626;
+        }
+      }
+      
+      .urgency-high { 
+        background: linear-gradient(135deg, #f59e0b 0%, #fbbf24 100%);
+        
+        .urgency-text {
+          color: #d97706;
+        }
+      }
+      
+      .urgency-medium { 
+        background: linear-gradient(135deg, #eab308 0%, #facc15 100%);
+        
+        .urgency-text {
+          color: #ca8a04;
+        }
+      }
+      
+      .urgency-low { 
+        background: linear-gradient(135deg, #22c55e 0%, #4ade80 100%);
+        
+        .urgency-text {
+          color: #16a34a;
+        }
+      }
     }
   }
 }
 
-/* 核心卡片区域样式 */
+// 脉冲动画
+@keyframes pulse {
+  0% {
+    transform: scale(1);
+    opacity: 0.3;
+  }
+  50% {
+    transform: scale(1.2);
+    opacity: 0.1;
+  }
+  100% {
+    transform: scale(1);
+    opacity: 0.3;
+  }
+}
+
+/* 核心卡片区域样式 - 现代化设计 */
 .core-cards-section {
-  margin-bottom: 30px;
+  margin-bottom: 32px;
   
   .cards-grid {
     display: grid;
-    grid-template-columns: repeat(3, 1fr);
-    gap: 20px;
+    grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
+    gap: 24px;
+    
+    @media (max-width: 1400px) {
+      grid-template-columns: repeat(2, 1fr);
+      gap: 22px;
+    }
     
-    @media (max-width: 1024px) {
+    @media (max-width: 1200px) {
       grid-template-columns: repeat(2, 1fr);
+      gap: 20px;
     }
     
     @media (max-width: 768px) {
       grid-template-columns: 1fr;
+      gap: 16px;
+    }
+    
+    @media (max-width: 480px) {
+      gap: 12px;
     }
   }
 }
 
-/* 核心卡片基础样式 */
+/* 核心卡片基础样式 - 现代化升级 */
 .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;
+  background: linear-gradient(135deg, #ffffff 0%, #fafbfc 100%);
+  border-radius: 16px;
+  padding: 24px;
+  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08), 0 1px 3px rgba(0, 0, 0, 0.05);
+  transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
+  border: 1px solid rgba(255, 255, 255, 0.8);
   display: flex;
   flex-direction: column;
   height: 100%;
+  position: relative;
+  overflow: hidden;
+  
+  // 添加微妙的背景纹理
+  &::before {
+    content: '';
+    position: absolute;
+    top: 0;
+    left: 0;
+    right: 0;
+    height: 4px;
+    background: linear-gradient(90deg, #007aff 0%, #5ac8fa 50%, #34c759 100%);
+    opacity: 0;
+    transition: opacity 0.3s ease;
+  }
   
   &:hover {
-    box-shadow: $ios-shadow-lg;
-    transform: translateY(-2px);
+    box-shadow: 0 8px 32px rgba(0, 0, 0, 0.12), 0 2px 8px rgba(0, 0, 0, 0.08);
+    transform: translateY(-4px) scale(1.02);
+    border-color: rgba(0, 122, 255, 0.2);
+    
+    &::before {
+      opacity: 1;
+    }
+  }
+  
+  // 紧急任务卡片特殊样式
+  &.urgent-card {
+    background: linear-gradient(135deg, #fff5f5 0%, #fef2f2 100%);
+    border-color: rgba(239, 68, 68, 0.2);
+    
+    &::before {
+      background: linear-gradient(90deg, #ef4444 0%, #f87171 100%);
+      opacity: 1;
+    }
+    
+    &:hover {
+      border-color: rgba(239, 68, 68, 0.3);
+      box-shadow: 0 8px 32px rgba(239, 68, 68, 0.15), 0 2px 8px rgba(239, 68, 68, 0.1);
+    }
+  }
+  
+  // 工作量卡片特殊样式
+  &.workload-card {
+    background: linear-gradient(135deg, #f0fdf4 0%, #f7fee7 100%);
+    border-color: rgba(34, 197, 94, 0.2);
+    
+    &::before {
+      background: linear-gradient(90deg, #22c55e 0%, #4ade80 100%);
+      opacity: 0.8;
+    }
+    
+    &:hover {
+      border-color: rgba(34, 197, 94, 0.3);
+      box-shadow: 0 8px 32px rgba(34, 197, 94, 0.15), 0 2px 8px rgba(34, 197, 94, 0.1);
+    }
   }
   
   .card-header {
     display: flex;
     justify-content: space-between;
     align-items: flex-start;
-    margin-bottom: 16px;
+    margin-bottom: 20px;
+    position: relative;
+    z-index: 1;
     
     .card-badge {
-      padding: 4px 12px;
-      border-radius: $ios-radius-full;
-      font-size: 12px;
-      font-weight: $ios-font-weight-medium;
+      padding: 6px 14px;
+      border-radius: 20px;
+      font-size: 11px;
+      font-weight: 600;
+      letter-spacing: 0.5px;
+      text-transform: uppercase;
       color: $ios-text-secondary;
-      background-color: $ios-background-secondary;
+      background: rgba(0, 0, 0, 0.05);
+      backdrop-filter: blur(10px);
+      border: 1px solid rgba(255, 255, 255, 0.2);
       
       &.urgent {
-        background-color: rgba(255, 59, 48, 0.1);
-        color: $ios-danger;
-        border: 1px solid $ios-danger;
+        background: linear-gradient(135deg, rgba(239, 68, 68, 0.15) 0%, rgba(248, 113, 113, 0.1) 100%);
+        color: #dc2626;
+        border-color: rgba(239, 68, 68, 0.3);
+        box-shadow: 0 2px 8px rgba(239, 68, 68, 0.2);
       }
       
       &.workload {
-        background-color: rgba(52, 199, 89, 0.1);
-        color: $ios-success;
-        border: 1px solid $ios-success;
+        background: linear-gradient(135deg, rgba(34, 197, 94, 0.15) 0%, rgba(74, 222, 128, 0.1) 100%);
+        color: #16a34a;
+        border-color: rgba(34, 197, 94, 0.3);
+        box-shadow: 0 2px 8px rgba(34, 197, 94, 0.2);
       }
       
       &.overdue {
-        background-color: rgba(255, 149, 0, 0.1);
-        color: $ios-warning;
-        border: 1px solid $ios-warning;
+        background: linear-gradient(135deg, rgba(245, 158, 11, 0.15) 0%, rgba(251, 191, 36, 0.1) 100%);
+        color: #d97706;
+        border-color: rgba(245, 158, 11, 0.3);
+        box-shadow: 0 2px 8px rgba(245, 158, 11, 0.2);
       }
     }
     
     h3 {
-      font-size: 17px;
+      font-size: 18px;
+      font-weight: 600;
       color: $ios-text-primary;
       margin: 0;
       font-family: $ios-font-family;
       flex: 1;
-      margin-right: 10px;
+      margin-right: 12px;
+      line-height: 1.4;
+      letter-spacing: -0.2px;
     }
   }
   
   .card-content {
     flex: 1;
-    margin-bottom: 16px;
+    margin-bottom: 20px;
     
-    .project-name,
-    .deadline,
-    .countdown,
-    .workload-status {
-      font-size: 14px;
-      color: $ios-text-secondary;
-      margin: 6px 0;
+    .project-name {
+      font-size: 15px;
+      color: #6b7280;
+      margin: 8px 0;
+      font-weight: 500;
+      display: flex;
+      align-items: center;
+      
+      &::before {
+        content: '📁';
+        margin-right: 8px;
+        font-size: 14px;
+      }
     }
     
-    .deadline.overdue {
-      color: $ios-danger;
+    .deadline {
+      font-size: 14px;
+      color: #9ca3af;
+      margin: 8px 0;
+      display: flex;
+      align-items: center;
+      
+      &::before {
+        content: '📅';
+        margin-right: 8px;
+        font-size: 13px;
+      }
+      
+      &.overdue {
+        color: #dc2626;
+        font-weight: 600;
+        
+        &::before {
+          content: '⚠️';
+        }
+      }
     }
     
     .countdown {
-      color: $ios-warning;
-      font-weight: $ios-font-weight-medium;
+      font-size: 14px;
+      color: #f59e0b;
+      font-weight: 600;
+      margin: 8px 0;
+      display: flex;
+      align-items: center;
+      
+      &::before {
+        content: '⏰';
+        margin-right: 8px;
+        font-size: 13px;
+      }
+    }
+    
+    .workload-status {
+      font-size: 15px;
+      color: #6b7280;
+      margin: 8px 0;
+      font-weight: 500;
     }
   }
   
   .card-actions {
     display: flex;
-    gap: 10px;
+    gap: 12px;
     
     button {
-      padding: 8px 16px;
+      padding: 12px 20px;
       border: none;
-      border-radius: $ios-radius-md;
+      border-radius: 12px;
       font-size: 14px;
+      font-weight: 600;
       cursor: pointer;
-      transition: all 0.3s ease;
-      font-weight: $ios-font-weight-medium;
+      transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
       font-family: $ios-font-family;
       flex: 1;
+      position: relative;
+      overflow: hidden;
+      
+      &::before {
+        content: '';
+        position: absolute;
+        top: 0;
+        left: -100%;
+        width: 100%;
+        height: 100%;
+        background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
+        transition: left 0.5s;
+      }
+      
+      &:hover::before {
+        left: 100%;
+      }
     }
     
     .btn-primary {
-      background-color: $ios-primary;
+      background: linear-gradient(135deg, #007aff 0%, #5ac8fa 100%);
       color: white;
+      box-shadow: 0 4px 12px rgba(0, 122, 255, 0.3);
       
       &:hover {
-        background-color: color-mix(in srgb, $ios-primary 90%, black);
-        transform: translateY(-1px);
-        box-shadow: $ios-shadow-md;
+        background: linear-gradient(135deg, #0056cc 0%, #4aa3d9 100%);
+        transform: translateY(-2px);
+        box-shadow: 0 6px 20px rgba(0, 122, 255, 0.4);
+      }
+      
+      &:active {
+        transform: translateY(0);
       }
     }
     
     .btn-secondary {
-      background-color: $ios-background-secondary;
+      background: rgba(0, 0, 0, 0.05);
       color: $ios-text-primary;
-      border: 1px solid $ios-border;
+      border: 1px solid rgba(0, 0, 0, 0.1);
+      backdrop-filter: blur(10px);
       
       &:hover {
-        background-color: color-mix(in srgb, $ios-background-secondary 70%, white);
+        background: rgba(0, 0, 0, 0.08);
+        border-color: rgba(0, 0, 0, 0.15);
+        transform: translateY(-1px);
+        box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
       }
     }
   }
 }
 
-/* 工作量卡片特定样式 */
+/* 工作量卡片特定样式 - 现代化升级 */
 .workload-card {
   .workload-indicator {
     display: flex;
     justify-content: center;
     align-items: center;
-    margin: 20px 0;
+    margin: 24px 0;
   }
   
   .workload-circle {
-    width: 120px;
-    height: 120px;
+    width: 140px;
+    height: 140px;
     border-radius: 50%;
     display: flex;
     justify-content: center;
     align-items: center;
-    box-shadow: $ios-shadow-md;
-    transition: all 0.3s ease;
+    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12), inset 0 2px 4px rgba(255, 255, 255, 0.8);
+    transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
+    position: relative;
+    background: linear-gradient(135deg, #ffffff 0%, #f8fafc 100%);
+    border: 3px solid transparent;
+    
+    // 动态边框效果
+    &::before {
+      content: '';
+      position: absolute;
+      top: -3px;
+      left: -3px;
+      right: -3px;
+      bottom: -3px;
+      border-radius: 50%;
+      background: linear-gradient(135deg, #22c55e 0%, #4ade80 50%, #10b981 100%);
+      z-index: -1;
+      opacity: 0.8;
+    }
+    
+    // 内部光晕效果
+    &::after {
+      content: '';
+      position: absolute;
+      top: 10%;
+      left: 10%;
+      right: 10%;
+      bottom: 10%;
+      border-radius: 50%;
+      background: radial-gradient(circle at 30% 30%, rgba(255, 255, 255, 0.6) 0%, transparent 50%);
+      pointer-events: none;
+    }
+    
+    &:hover {
+      transform: scale(1.05);
+      box-shadow: 0 12px 32px rgba(0, 0, 0, 0.15), inset 0 2px 4px rgba(255, 255, 255, 0.9);
+      
+      &::before {
+        opacity: 1;
+        background: linear-gradient(135deg, #16a34a 0%, #22c55e 50%, #4ade80 100%);
+      }
+    }
     
     .workload-percentage {
-      font-size: 28px;
-      font-weight: $ios-font-weight-bold;
-      color: white;
+      font-size: 32px;
+      font-weight: 700;
+      color: #1f2937;
       font-family: $ios-font-family;
+      text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
+      position: relative;
+      z-index: 1;
     }
   }
 }

+ 153 - 0
src/app/pages/designer/dashboard/interactive-effects.scss

@@ -0,0 +1,153 @@
+// 交互效果样式
+// 全局交互动画变量
+:root {
+  --transition-fast: 0.15s ease-out;
+  --transition-normal: 0.25s ease-out;
+  --transition-slow: 0.35s ease-out;
+}
+
+// 卡片交互效果增强
+.core-card {
+  transition: all var(--transition-normal);
+  
+  &:hover {
+    transform: translateY(-2px) scale(1.02);
+    box-shadow: 0 8px 32px rgba(0, 0, 0, 0.12), 0 2px 8px rgba(0, 0, 0, 0.08);
+  }
+  
+  &:active {
+    transform: translateY(0) scale(0.98);
+    transition: all var(--transition-fast);
+  }
+}
+
+// 按钮交互效果
+.nav-btn, .view-toggle button, .add-shift-btn {
+  transition: all var(--transition-normal);
+  position: relative;
+  overflow: hidden;
+  
+  &::before {
+    content: '';
+    position: absolute;
+    top: 50%;
+    left: 50%;
+    width: 0;
+    height: 0;
+    background: rgba(255, 255, 255, 0.3);
+    border-radius: 50%;
+    transform: translate(-50%, -50%);
+    transition: width var(--transition-normal), height var(--transition-normal);
+  }
+  
+  &:hover::before {
+    width: 300px;
+    height: 300px;
+  }
+  
+  &:active {
+    transform: scale(0.95);
+  }
+}
+
+// 列表行交互效果
+.list-row {
+  transition: all var(--transition-normal);
+  
+  &:hover {
+    background: rgba(0, 122, 255, 0.05);
+    transform: translateX(4px);
+    box-shadow: 0 2px 8px rgba(0, 122, 255, 0.1);
+  }
+}
+
+// 工作量圆环特殊效果
+.workload-ring {
+  transition: all var(--transition-normal);
+  
+  &:hover {
+    transform: scale(1.05);
+    filter: brightness(1.1);
+  }
+}
+
+// 紧急任务卡片特殊效果
+.urgent-task {
+  position: relative;
+  
+  &::after {
+    content: '';
+    position: absolute;
+    top: 0;
+    left: 0;
+    right: 0;
+    bottom: 0;
+    background: linear-gradient(45deg, transparent 30%, rgba(255, 59, 48, 0.1) 50%, transparent 70%);
+    opacity: 0;
+    transition: opacity var(--transition-normal);
+  }
+  
+  &:hover::after {
+    opacity: 1;
+  }
+}
+
+// 加载状态效果
+.loading {
+  position: relative;
+  
+  &::before {
+    content: '';
+    position: absolute;
+    top: 0;
+    left: -100%;
+    width: 100%;
+    height: 100%;
+    background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.4), transparent);
+    animation: shimmer 1.5s infinite;
+  }
+}
+
+@keyframes shimmer {
+  0% { left: -100%; }
+  100% { left: 100%; }
+}
+
+// 成功状态动画
+.success-state {
+  animation: successPulse 0.6s ease-out;
+}
+
+@keyframes successPulse {
+  0% { transform: scale(1); }
+  50% { transform: scale(1.05); }
+  100% { transform: scale(1); }
+}
+
+// 错误状态动画
+.error-state {
+  animation: errorShake 0.5s ease-out;
+}
+
+@keyframes errorShake {
+  0%, 100% { transform: translateX(0); }
+  25% { transform: translateX(-4px); }
+  75% { transform: translateX(4px); }
+}
+
+// 微交互反馈
+.interactive-element {
+  &:focus-visible {
+    outline: 2px solid rgba(0, 122, 255, 0.6);
+    outline-offset: 2px;
+  }
+}
+
+// 减少动画偏好设置支持
+@media (prefers-reduced-motion: reduce) {
+  * {
+    animation-duration: 0.01ms !important;
+    animation-iteration-count: 1 !important;
+    transition-duration: 0.01ms !important;
+  }
+}

+ 318 - 0
src/app/pages/designer/dashboard/responsive-layout.scss

@@ -0,0 +1,318 @@
+/* 响应式布局优化 - 现代化间距和对齐 */
+
+/* 大屏幕优化 (1400px+) */
+@media (min-width: 1400px) {
+  .dashboard-container {
+    max-width: 1400px;
+    margin: 0 auto;
+    padding: 32px 24px;
+  }
+  
+  .dashboard-main {
+    gap: 40px;
+  }
+  
+  .core-cards-section .cards-grid {
+    grid-template-columns: repeat(3, 1fr);
+    gap: 28px;
+  }
+}
+
+/* 中大屏幕 (1200px - 1399px) */
+@media (max-width: 1399px) and (min-width: 1200px) {
+  .dashboard-container {
+    padding: 24px 20px;
+  }
+  
+  .dashboard-main {
+    gap: 32px;
+  }
+}
+
+/* 中等屏幕 (768px - 1199px) */
+@media (max-width: 1199px) and (min-width: 768px) {
+  .dashboard-container {
+    padding: 20px 16px;
+  }
+  
+  .dashboard-header {
+    margin-bottom: 24px;
+    padding: 16px 0;
+    
+    h1 {
+      font-size: 28px;
+      letter-spacing: -0.6px;
+    }
+  }
+  
+  .dashboard-nav {
+    margin-bottom: 20px;
+    
+    .nav-btn {
+      padding: 14px 20px;
+      font-size: 15px;
+    }
+  }
+  
+  .view-toggle {
+    margin-bottom: 12px;
+    
+    .toggle-btn {
+      padding: 10px 16px;
+      font-size: 13px;
+    }
+  }
+  
+  .dashboard-main {
+    gap: 28px;
+  }
+}
+
+/* 小屏幕 (481px - 767px) */
+@media (max-width: 767px) and (min-width: 481px) {
+  .dashboard-container {
+    padding: 16px 12px;
+  }
+  
+  .dashboard-header {
+    margin-bottom: 20px;
+    padding: 12px 0;
+    
+    h1 {
+      font-size: 24px;
+      letter-spacing: -0.4px;
+    }
+    
+    .subtitle {
+      font-size: 14px;
+    }
+  }
+  
+  .dashboard-nav {
+    margin-bottom: 16px;
+    padding: 4px;
+    
+    .nav-btn {
+      padding: 12px 16px;
+      font-size: 14px;
+      letter-spacing: 0.2px;
+    }
+  }
+  
+  .view-toggle {
+    margin-bottom: 12px;
+    gap: 6px;
+    
+    .toggle-btn {
+      padding: 8px 12px;
+      font-size: 12px;
+      border-radius: 8px;
+    }
+  }
+  
+  .dashboard-main {
+    gap: 24px;
+    padding: 0 2px;
+  }
+  
+  .list-section {
+    .list-header {
+      padding: 12px 16px;
+      font-size: 12px;
+      grid-template-columns: 100px 1fr 1fr 100px 140px 100px 120px;
+    }
+    
+    .list-body .list-row {
+      padding: 12px 16px;
+      grid-template-columns: 100px 1fr 1fr 100px 140px 100px 120px;
+      
+      .col {
+        font-size: 13px;
+      }
+      
+      .actions-col {
+        gap: 8px;
+        
+        .btn-link {
+          padding: 6px 10px;
+          font-size: 12px;
+        }
+      }
+    }
+  }
+  
+  .core-card {
+    padding: 20px;
+    border-radius: 12px;
+    
+    .card-header {
+      margin-bottom: 16px;
+      
+      .card-title {
+        font-size: 16px;
+      }
+      
+      .card-badge {
+        padding: 4px 8px;
+        font-size: 11px;
+      }
+    }
+    
+    .card-content {
+      gap: 12px;
+      
+      .project-name {
+        font-size: 14px;
+      }
+      
+      .deadline, .countdown {
+        font-size: 12px;
+      }
+    }
+  }
+}
+
+/* 超小屏幕 (480px及以下) */
+@media (max-width: 480px) {
+  .dashboard-container {
+    padding: 12px 8px;
+  }
+  
+  .dashboard-header {
+    margin-bottom: 16px;
+    padding: 8px 0;
+    
+    h1 {
+      font-size: 20px;
+      letter-spacing: -0.3px;
+    }
+    
+    .subtitle {
+      font-size: 13px;
+    }
+  }
+  
+  .dashboard-nav {
+    margin-bottom: 12px;
+    padding: 3px;
+    
+    .nav-btn {
+      padding: 10px 12px;
+      font-size: 13px;
+    }
+  }
+  
+  .view-toggle {
+    margin-bottom: 8px;
+    gap: 4px;
+    
+    .toggle-btn {
+      padding: 6px 10px;
+      font-size: 11px;
+      border-radius: 6px;
+    }
+  }
+  
+  .dashboard-main {
+    gap: 20px;
+    padding: 0 1px;
+  }
+  
+  .core-card {
+    padding: 16px;
+    border-radius: 10px;
+    
+    .card-header {
+      margin-bottom: 12px;
+      
+      .card-title {
+        font-size: 15px;
+      }
+      
+      .card-badge {
+        padding: 3px 6px;
+        font-size: 10px;
+      }
+    }
+    
+    .card-content {
+      gap: 10px;
+      
+      .project-name {
+        font-size: 13px;
+      }
+      
+      .deadline, .countdown {
+        font-size: 11px;
+      }
+    }
+  }
+  
+  .list-section {
+    border-radius: 12px;
+    
+    .list-header {
+      padding: 10px 12px;
+      font-size: 11px;
+      grid-template-columns: 80px 1fr 80px 100px 80px;
+      
+      // 隐藏部分列以适应小屏幕
+      .col:nth-child(3),
+      .col:nth-child(6) {
+        display: none;
+      }
+    }
+    
+    .list-body .list-row {
+      padding: 10px 12px;
+      grid-template-columns: 80px 1fr 80px 100px 80px;
+      
+      .col {
+        font-size: 12px;
+        
+        &:nth-child(3),
+        &:nth-child(6) {
+          display: none;
+        }
+      }
+      
+      .actions-col {
+        gap: 6px;
+        
+        .btn-link {
+          padding: 4px 8px;
+          font-size: 11px;
+        }
+      }
+    }
+  }
+}
+
+/* 横屏手机优化 */
+@media (max-height: 500px) and (orientation: landscape) {
+  .dashboard-header {
+    margin-bottom: 12px;
+    padding: 8px 0;
+    
+    h1 {
+      font-size: 18px;
+    }
+  }
+  
+  .dashboard-nav {
+    margin-bottom: 8px;
+    
+    .nav-btn {
+      padding: 8px 16px;
+      font-size: 12px;
+    }
+  }
+  
+  .dashboard-main {
+    gap: 16px;
+  }
+  
+  .core-card {
+    padding: 12px;
+  }
+}

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

@@ -67,6 +67,7 @@
             @if (ganttMode === 'project') { 颜色标识紧急程度:红=高,橙=中,绿=低 } @else { 设计师排班:按项目数量由排满到空闲排列 }
           </div>
           <div class="scale-switch">
+            <button [class.active]="ganttScale === 'day'" (click)="setGanttScale('day')">日</button>
             <button [class.active]="ganttScale === 'week'" (click)="setGanttScale('week')">周</button>
             <button [class.active]="ganttScale === 'month'" (click)="setGanttScale('month')">月</button>
           </div>

+ 277 - 43
src/app/pages/team-leader/dashboard/dashboard.ts

@@ -114,10 +114,14 @@ export class Dashboard implements OnInit, OnDestroy {
   
   // 设计师画像(用于智能推荐)
   designerProfiles: any[] = [
-    { id: 'zhang', name: '张三', skills: ['现代风格', '北欧风格'], workload: 70, avgRating: 4.5, experience: 3 },
-    { id: 'li', name: '李四', skills: ['新中式', '宋式风格'], workload: 45, avgRating: 4.8, experience: 5 },
-    { id: 'wang', name: '王五', skills: ['北欧风格', '日式风格'], workload: 85, avgRating: 4.2, experience: 2 },
-    { id: 'zhao', name: '赵六', skills: ['现代风格', '轻奢风格'], workload: 30, avgRating: 4.6, experience: 4 }
+    { id: 'zhang', name: '张三', skills: ['现代风格', '北欧风格'], workload: 95, avgRating: 4.5, experience: 3 },
+    { id: 'li', name: '李四', skills: ['新中式', '宋式风格'], workload: 25, avgRating: 4.8, experience: 5 },
+    { id: 'wang', name: '王五', skills: ['北欧风格', '日式风格'], workload: 75, avgRating: 4.2, experience: 2 },
+    { id: 'zhao', name: '赵六', skills: ['现代风格', '轻奢风格'], workload: 15, avgRating: 4.6, experience: 4 },
+    { id: 'sun', name: '孙七', skills: ['简约风格', '工业风格'], workload: 35, avgRating: 4.3, experience: 3 },
+    { id: 'zhou', name: '周八', skills: ['欧式风格', '美式风格'], workload: 5, avgRating: 4.7, experience: 6 },
+    { id: 'wu', name: '吴九', skills: ['地中海风格', '田园风格'], workload: 60, avgRating: 4.4, experience: 4 },
+    { id: 'chen', name: '陈十', skills: ['现代简约', '新古典'], workload: 0, avgRating: 4.9, experience: 7 }
   ];
 
   // 10个项目阶段
@@ -150,7 +154,7 @@ export class Dashboard implements OnInit, OnDestroy {
   private workloadChart: any | null = null;
   workloadDimension: 'designer' | 'member' = 'designer';
   // 甘特时间尺度:仅周/月
-  ganttScale: 'week' | 'month' = 'week';
+  ganttScale: 'day' | 'week' | 'month' = 'week';
   // 新增:甘特模式(项目 / 设计师排班)
   ganttMode: 'project' | 'designer' = 'project';
 
@@ -341,7 +345,7 @@ export class Dashboard implements OnInit, OnDestroy {
 
     // ===== 追加生成示例数据:保证总量达到100条 =====
     const stageIds = this.projectStages.map(s => s.id);
-    const designers = ['张三','李四','王五','赵六','钱七','孙八','周九','吴十','郑一','冯二','陈三','褚四'];
+    const designers = ['张三','李四','王五','赵六','孙七','周八','吴九','陈十'];
     const statusMap: Record<string, string> = {
       pendingApproval: '待确认',
       pendingAssignment: '待分配',
@@ -355,26 +359,32 @@ export class Dashboard implements OnInit, OnDestroy {
       delivery: '已完成'
     };
 
-    for (let i = 8; i <= 100; i++) {
-      const stageIndex = (i - 1) % stageIds.length;
+    // 为有项目的设计师分配项目
+    const busyDesigners = ['张三', '王五', '吴九']; // 高负荷设计师
+    const moderateDesigners = ['孙七']; // 中等负荷设计师
+    const idleDesigners = ['李四', '赵六', '周八', '陈十']; // 空闲设计师
+
+    // 为忙碌的设计师分配更多项目
+    for (let i = 8; i <= 30; i++) {
+      const designerIndex = (i - 8) % busyDesigners.length;
+      const designerName = busyDesigners[designerIndex];
+      const stageIndex = (i - 1) % 7 + 3; // 主要在进行中的阶段
       const currentStage = stageIds[stageIndex];
       const type: 'soft' | 'hard' = i % 2 === 0 ? 'soft' : 'hard';
-      const urgency: 'high' | 'medium' | 'low' = i % 5 === 0 ? 'high' : (i % 3 === 0 ? 'medium' : 'low');
-      const isOverdue = ['planning','modeling','rendering','postProduction','review','revision','delivery'].includes(currentStage) ? i % 7 === 0 : false;
-      const overdueDays = isOverdue ? (i % 10) + 1 : 0;
-      const hasDesigner = !['pendingApproval', 'pendingAssignment'].includes(currentStage);
-      const designerName = hasDesigner ? designers[i % designers.length] : '';
+      const urgency: 'high' | 'medium' | 'low' = i % 4 === 0 ? 'high' : (i % 3 === 0 ? 'medium' : 'low');
+      const isOverdue = i % 8 === 0;
+      const overdueDays = isOverdue ? (i % 5) + 1 : 0;
       const status = statusMap[currentStage] || '进行中';
       const expectedEndDate = new Date();
-      const daysOffset = isOverdue ? - (overdueDays + (i % 5)) : ((i % 20) + 3);
+      const daysOffset = isOverdue ? -(overdueDays + (i % 3)) : ((i % 15) + 5);
       expectedEndDate.setDate(expectedEndDate.getDate() + daysOffset);
       const daysToDeadline = Math.ceil((expectedEndDate.getTime() - Date.now()) / (1000 * 60 * 60 * 24));
       const dueSoon = !isOverdue && daysToDeadline >= 0 && daysToDeadline <= 3;
-      const memberType: 'vip' | 'normal' = i % 4 === 0 ? 'vip' : 'normal';
+      const memberType: 'vip' | 'normal' = i % 5 === 0 ? 'vip' : 'normal';
 
       this.projects.push({
         id: `proj-${String(i).padStart(3, '0')}`,
-        name: `${type === 'soft' ? '软装' : '硬装'}示例项目 ${i}`,
+        name: `${designerName}负责的${type === 'soft' ? '软装' : '硬装'}项目 ${i}`,
         type,
         memberType,
         designerName,
@@ -389,6 +399,66 @@ export class Dashboard implements OnInit, OnDestroy {
         phases: []
       });
     }
+
+    // 为中等负荷设计师分配少量项目
+    for (let i = 31; i <= 35; i++) {
+      const designerName = moderateDesigners[0];
+      const stageIndex = (i - 1) % 5 + 4; // 中间阶段
+      const currentStage = stageIds[stageIndex];
+      const type: 'soft' | 'hard' = i % 2 === 0 ? 'soft' : 'hard';
+      const urgency: 'high' | 'medium' | 'low' = 'medium';
+      const status = statusMap[currentStage] || '进行中';
+      const expectedEndDate = new Date();
+      expectedEndDate.setDate(expectedEndDate.getDate() + (i % 10) + 7);
+      const memberType: 'vip' | 'normal' = 'normal';
+
+      this.projects.push({
+        id: `proj-${String(i).padStart(3, '0')}`,
+        name: `${designerName}负责的${type === 'soft' ? '软装' : '硬装'}项目 ${i}`,
+        type,
+        memberType,
+        designerName,
+        status,
+        expectedEndDate,
+        deadline: expectedEndDate,
+        isOverdue: false,
+        overdueDays: 0,
+        dueSoon: false,
+        urgency,
+        currentStage,
+        phases: []
+      });
+    }
+
+    // 空闲设计师不分配项目,或只分配很少的已完成项目
+    for (let i = 36; i <= 40; i++) {
+      const designerIndex = (i - 36) % idleDesigners.length;
+      const designerName = idleDesigners[designerIndex];
+      const currentStage = 'delivery'; // 已完成的项目
+      const type: 'soft' | 'hard' = i % 2 === 0 ? 'soft' : 'hard';
+      const urgency: 'high' | 'medium' | 'low' = 'low';
+      const status = '已完成';
+      const expectedEndDate = new Date();
+      expectedEndDate.setDate(expectedEndDate.getDate() - (i % 10) - 5); // 过去的日期
+      const memberType: 'vip' | 'normal' = 'normal';
+
+      this.projects.push({
+        id: `proj-${String(i).padStart(3, '0')}`,
+        name: `${designerName}已完成的${type === 'soft' ? '软装' : '硬装'}项目 ${i}`,
+        type,
+        memberType,
+        designerName,
+        status,
+        expectedEndDate,
+        deadline: expectedEndDate,
+        isOverdue: false,
+        overdueDays: 0,
+        dueSoon: false,
+        urgency,
+        currentStage,
+        phases: []
+      });
+    }
     // ===== 示例数据生成结束 =====
 
     // 统一补齐真实时间字段(deadline/createdAt),以真实字段贯通筛选与甘特
@@ -696,7 +766,7 @@ export class Dashboard implements OnInit, OnDestroy {
   }
 
   // 设置甘特时间尺度
-  setGanttScale(scale: 'week' | 'month'): void {
+  setGanttScale(scale: 'day' | 'week' | 'month'): void {
     if (this.ganttScale !== scale) {
       this.ganttScale = scale;
       this.updateGantt();
@@ -960,28 +1030,45 @@ export class Dashboard implements OnInit, OnDestroy {
     const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
     const todayTs = today.getTime();
 
-    // 时间轴按当前周/月
+    // 时间轴按当前周/月/日
     let xMin: number;
     let xMax: number;
     let xSplitNumber: number;
     let xLabelFormatter: (value: number) => string;
-    if (this.ganttScale === 'week') {
-      const day = today.getDay();
-      const diffToMonday = (day === 0 ? 6 : day - 1);
-      const startOfWeek = new Date(today.getTime() - diffToMonday * DAY);
-      const endOfWeek = new Date(startOfWeek.getTime() + 7 * DAY - 1);
+    if (this.ganttScale === 'day') {
+      // 日视图:显示今日24小时
+      const startOfDay = new Date(today.getTime());
+      const endOfDay = new Date(today.getTime() + DAY - 1);
+      xMin = startOfDay.getTime();
+      xMax = endOfDay.getTime();
+      xSplitNumber = 24;
+      xLabelFormatter = (val) => `${new Date(val).getHours()}:00`;
+    } else if (this.ganttScale === 'week') {
+      // 周视图:从今天开始显示未来7天的具体日期
+      const startOfWeek = new Date(today.getTime());
+      const endOfWeek = new Date(today.getTime() + 7 * DAY - 1);
       xMin = startOfWeek.getTime();
       xMax = endOfWeek.getTime();
       xSplitNumber = 7;
-      const WEEK_LABELS = ['周日','周一','周二','周三','周四','周五','周六'];
-      xLabelFormatter = (val) => WEEK_LABELS[new Date(val).getDay()];
+      xLabelFormatter = (val) => {
+        const date = new Date(val);
+        const month = date.getMonth() + 1;
+        const day = date.getDate();
+        return `${month}月${day}日`;
+      };
     } else {
+      // 月视图:从当前月份开始显示未来几个月
       const startOfMonth = new Date(today.getFullYear(), today.getMonth(), 1);
-      const endOfMonth = new Date(today.getFullYear(), today.getMonth() + 1, 0, 23, 59, 59, 999);
+      const endOfMonth = new Date(today.getFullYear(), today.getMonth() + 3, 0, 23, 59, 59, 999); // 显示未来3个月
       xMin = startOfMonth.getTime();
       xMax = endOfMonth.getTime();
-      xSplitNumber = 4;
-      xLabelFormatter = (val) => `第${Math.ceil(new Date(val).getDate() / 7)}周`;
+      xSplitNumber = 3;
+      xLabelFormatter = (val) => {
+        const date = new Date(val);
+        const year = date.getFullYear();
+        const month = date.getMonth() + 1;
+        return `${year}年${month}月`;
+      };
     }
 
     // 仅统计已分配项目
@@ -1001,18 +1088,28 @@ export class Dashboard implements OnInit, OnDestroy {
 
     const categories = sortedDesigners;
 
-    // 工作量等级(用于左侧小圆点颜色)
+    // 工作量等级(用于左侧小圆点颜色和负荷状态判断
     const workloadLevelMap: Record<string, 'high'|'medium'|'low'> = {} as any;
+    const workloadStatusMap: Record<string, 'overloaded'|'busy'|'available'> = {} as any;
     categories.forEach(name => {
       const cnt = busyCountMap[name] || 0;
-      workloadLevelMap[name] = cnt >= 5 ? 'high' : (cnt >= 3 ? 'medium' : 'low');
+      if (cnt >= 5) {
+        workloadLevelMap[name] = 'high';
+        workloadStatusMap[name] = 'overloaded'; // 不宜派单
+      } else if (cnt >= 3) {
+        workloadLevelMap[name] = 'medium';
+        workloadStatusMap[name] = 'busy'; // 适度忙碌
+      } else {
+        workloadLevelMap[name] = 'low';
+        workloadStatusMap[name] = 'available'; // 可接单
+      }
     });
 
-    // 条形颜色仍按项目紧急度
+    // 条形颜色按项目紧急度,增强高负荷时段的视觉效果
     const colorByUrgency: Record<'high'|'medium'|'low', string> = {
-      high: '#ef4444',
-      medium: '#f59e0b',
-      low: '#22c55e'
+      high: '#dc2626', // 更深的红色,突出高紧急度
+      medium: '#ea580c', // 更深的橙色
+      low: '#16a34a' // 更深的绿色
     } as const;
 
     const data = assigned.flatMap(p => {
@@ -1021,14 +1118,104 @@ export class Dashboard implements OnInit, OnDestroy {
       const start = p.createdAt ? new Date(p.createdAt).getTime() : end - baseDays * DAY;
       const yIndex = categories.indexOf(p.designerName);
       if (yIndex === -1) return [] as any[];
-      const color = colorByUrgency[p.urgency] || '#60a5fa';
+      
+      // 根据设计师工作负荷状态调整项目条的视觉效果
+      const workloadStatus = workloadStatusMap[p.designerName];
+      let color = colorByUrgency[p.urgency] || '#60a5fa';
+      let borderWidth = 1;
+      let borderColor = 'transparent';
+      
+      // 高负荷时段增强视觉效果
+      if (workloadStatus === 'overloaded') {
+        borderWidth = 3;
+        borderColor = '#991b1b'; // 深红色边框
+        // 对于超负荷状态,使用更深的红色调
+        if (p.urgency === 'high') {
+          color = '#7f1d1d'; // 深红色
+        } else if (p.urgency === 'medium') {
+          color = '#c2410c'; // 深橙色
+        } else {
+          color = '#dc2626'; // 红色(即使是低紧急度也用红色表示超负荷)
+        }
+      }
+      
       return [{
         name: p.name,
-        value: [yIndex, start, end, p.designerName, p.urgency, p.memberType, p.currentStage],
-        itemStyle: { color }
+        value: [yIndex, start, end, p.designerName, p.urgency, p.memberType, p.currentStage, workloadStatus],
+        itemStyle: { 
+          color,
+          borderWidth,
+          borderColor,
+          opacity: workloadStatus === 'overloaded' ? 0.9 : 1.0
+        }
       }];
     });
 
+    // 生成空闲时段背景数据 - 只在真正空闲的时间段显示
+    const idleBackgroundData: any[] = [];
+    
+    categories.forEach((designerName, yIndex) => {
+      const designerProjects = byDesigner[designerName] || [];
+      const workloadStatus = workloadStatusMap[designerName];
+      
+      // 获取该设计师的所有项目时间段
+      const projectTimeRanges = designerProjects.map(p => {
+        const end = new Date(p.deadline).getTime();
+        const baseDays = p.type === 'hard' ? 30 : 14;
+        const start = p.createdAt ? new Date(p.createdAt).getTime() : end - baseDays * DAY;
+        return { start, end };
+      }).sort((a, b) => a.start - b.start);
+      
+      // 找出空闲时间段
+      const idleTimeRanges: { start: number; end: number }[] = [];
+      
+      if (projectTimeRanges.length === 0) {
+        // 完全没有项目,整个时间轴都是空闲
+        idleTimeRanges.push({ start: xMin, end: xMax });
+      } else {
+        // 检查项目之间的空隙
+        let currentTime = xMin;
+        
+        for (const range of projectTimeRanges) {
+          if (currentTime < range.start) {
+            // 在项目开始前有空闲时间
+            idleTimeRanges.push({ start: currentTime, end: range.start });
+          }
+          currentTime = Math.max(currentTime, range.end);
+        }
+        
+        // 检查最后一个项目后是否还有空闲时间
+        if (currentTime < xMax) {
+          idleTimeRanges.push({ start: currentTime, end: xMax });
+        }
+      }
+      
+      // 为每个空闲时间段创建背景数据
+      idleTimeRanges.forEach((idleRange, index) => {
+        // 只有当空闲时间段足够长时才显示(至少1天)
+        if (idleRange.end - idleRange.start >= DAY) {
+          let backgroundColor = 'transparent';
+          
+          if (workloadStatus === 'available') {
+            backgroundColor = 'rgba(34, 197, 94, 0.15)'; // 淡绿色背景表示空闲可接单
+          } else if (workloadStatus === 'overloaded') {
+            backgroundColor = 'rgba(239, 68, 68, 0.1)'; // 淡红色背景表示超负荷
+          }
+          
+          if (backgroundColor !== 'transparent') {
+            idleBackgroundData.push({
+              name: `${designerName}-空闲${index + 1}`,
+              value: [yIndex, idleRange.start, idleRange.end, designerName, 'background', workloadStatus],
+              itemStyle: { 
+                color: backgroundColor,
+                borderWidth: 0
+              }
+            });
+          }
+        }
+      });
+    });
+
     const prevOpt: any = (this.ganttChart as any).getOption ? (this.ganttChart as any).getOption() : null;
     const total = categories.length || 1;
     const visible = Math.min(total, 30);
@@ -1042,12 +1229,27 @@ export class Dashboard implements OnInit, OnDestroy {
         trigger: 'item',
         formatter: (params: any) => {
           const v = params.value;
+          if (v[4] === 'background') {
+            const workloadStatus = v[5];
+            const statusText = workloadStatus === 'available' ? '空闲可接单' : 
+                              workloadStatus === 'overloaded' ? '超负荷不宜派单' : '适度忙碌';
+            return `设计师:${v[3]}<br/>状态:${statusText}`;
+          }
           const start = new Date(v[1]);
           const end = new Date(v[2]);
-          return `项目:${params.name}<br/>设计师:${v[3]}<br/>阶段:${v[6]}<br/>起止:${start.toLocaleDateString()} ~ ${end.toLocaleDateString()}`;
+          const workloadStatus = v[7];
+          const statusText = workloadStatus === 'available' ? '可接单' : 
+                            workloadStatus === 'overloaded' ? '超负荷' : '适度忙碌';
+          return `项目:${params.name}<br/>设计师:${v[3]}(${statusText})<br/>阶段:${v[6]}<br/>起止:${start.toLocaleDateString()} ~ ${end.toLocaleDateString()}`;
         }
       },
-      grid: { left: 110, right: 64, top: 30, bottom: 30 },
+      legend: {
+        data: ['空闲可接单', '适度忙碌', '超负荷不宜派单', '高紧急', '中紧急', '低紧急'],
+        bottom: 0,
+        itemGap: 20,
+        textStyle: { fontSize: 12 }
+      },
+      grid: { left: 110, right: 64, top: 30, bottom: 60 },
       xAxis: {
         type: 'time',
         min: xMin,
@@ -1067,13 +1269,16 @@ export class Dashboard implements OnInit, OnDestroy {
           formatter: (val: string) => {
             const lvl = workloadLevelMap[val] || 'low';
             const count = busyCountMap[val] || 0;
+            const status = workloadStatusMap[val] || 'available';
             const text = val.length > 8 ? val.slice(0, 8) + '…' : val;
-            return `{${lvl}Dot|●} ${text}(${count}项)`;
+            const statusIcon = status === 'available' ? '🟢' : 
+                              status === 'overloaded' ? '🔴' : '🟡';
+            return `{${lvl}Dot|●} ${statusIcon} ${text}(${count}项)`;
           },
           rich: {
-            highDot: { color: '#ef4444' },
-            mediumDot: { color: '#f59e0b' },
-            lowDot: { color: '#22c55e' }
+            highDot: { color: '#dc2626' },
+            mediumDot: { color: '#ea580c' },
+            lowDot: { color: '#16a34a' }
           }
         },
         axisTick: { show: false },
@@ -1084,8 +1289,36 @@ export class Dashboard implements OnInit, OnDestroy {
         { type: 'inside', yAxisIndex: 0, zoomOnMouseWheel: true, moveOnMouseMove: true, moveOnMouseWheel: true }
       ],
       series: [
+        // 背景层 - 显示空闲时段
         {
           type: 'custom',
+          name: '工作负荷背景',
+          renderItem: (params: any, api: any) => {
+            const categoryIndex = api.value(0);
+            const start = api.coord([api.value(1), categoryIndex]);
+            const end = api.coord([api.value(2), categoryIndex]);
+            const height = api.size([0, 1])[1] * 0.8;
+            const rectShape = echarts.graphic.clipRectByRect({
+              x: start[0],
+              y: start[1] - height / 2,
+              width: Math.max(end[0] - start[0], 2),
+              height
+            }, {
+              x: params.coordSys.x,
+              y: params.coordSys.y,
+              width: params.coordSys.width,
+              height: params.coordSys.height
+            });
+            return rectShape ? { type: 'rect', shape: rectShape, style: api.style() } : undefined;
+          },
+          encode: { x: [1, 2], y: 0 },
+          data: idleBackgroundData,
+          z: 1
+        },
+        // 项目条层
+        {
+          type: 'custom',
+          name: '项目进度',
           renderItem: (params: any, api: any) => {
             const categoryIndex = api.value(0);
             const start = api.coord([api.value(1), categoryIndex]);
@@ -1108,6 +1341,7 @@ export class Dashboard implements OnInit, OnDestroy {
           data,
           itemStyle: { borderRadius: 4 },
           emphasis: { focus: 'self' },
+          z: 2,
           markLine: {
             silent: true,
             symbol: 'none',

+ 1 - 2
src/app/shared/components/consultation-order-panel/consultation-order-panel.component.html

@@ -278,5 +278,4 @@
   [isVisible]="showTeamAssignmentModal"
   (closeModal)="closeTeamAssignmentModal()"
   (confirmAssignment)="confirmTeamAssignment($event)">
-</app-team-assignment-modal>
-
+</app-team-assignment-modal>

+ 45 - 0
src/app/shared/components/upload-success-modal/upload-success-modal.component.html

@@ -139,6 +139,51 @@
                     查看完整报告
                   </button>
                 </div>
+
+                <!-- 颜色描述文字区域 -->
+                <div class="color-description-section">
+                  <div class="description-header">
+                    <h6>颜色描述文字</h6>
+                    <p>适用于即梦等AI工具的颜色描述</p>
+                  </div>
+                  
+                  <div class="description-content">
+                    <div class="description-text" 
+                         [class.has-content]="generateColorDescription()"
+                         #descriptionText>
+                      {{ generateColorDescription() || '暂无颜色分析结果' }}
+                    </div>
+                    
+                    @if (generateColorDescription()) {
+                      <button class="copy-description-btn" 
+                              (click)="copyColorDescription()"
+                              [class.copied]="copySuccess"
+                              title="复制颜色描述">
+                        @if (copySuccess) {
+                          <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
+                            <polyline points="20,6 9,17 4,12"></polyline>
+                          </svg>
+                          已复制
+                        } @else {
+                          <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
+                            <rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
+                            <path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
+                          </svg>
+                          复制
+                        }
+                      </button>
+                    }
+                  </div>
+                  
+                  <div class="description-tips">
+                    <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
+                      <circle cx="12" cy="12" r="10"></circle>
+                      <path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"></path>
+                      <line x1="12" y1="17" x2="12.01" y2="17"></line>
+                    </svg>
+                    <span>复制后可直接粘贴到即梦等AI工具中,用于生成对应颜色风格的室内设计</span>
+                  </div>
+                </div>
               </div>
             }
 

+ 250 - 3
src/app/shared/components/upload-success-modal/upload-success-modal.component.scss

@@ -45,26 +45,29 @@ $animation-easing: cubic-bezier(0.25, 0.8, 0.25, 1);
   box-shadow: 0 20px 60px rgba(0, 0, 0, 0.15);
   max-width: 600px;
   width: 100%;
-  max-height: 90vh;
+  max-height: 80vh; // 减少最大高度,为滚动留出更多空间
   overflow: hidden;
   position: relative;
   transform-origin: center;
+  display: flex;
+  flex-direction: column;
   
   // 响应式调整
   @media (max-width: $mobile-breakpoint) {
     max-width: 100%;
     border-radius: 16px 16px 0 0;
-    max-height: 85vh;
+    max-height: 75vh; // 移动端进一步减少高度
     margin-top: auto;
   }
   
   @media (min-width: $mobile-breakpoint) and (max-width: $tablet-breakpoint) {
     max-width: 90%;
-    max-height: 85vh;
+    max-height: 78vh; // 平板端适中的高度
   }
   
   @media (min-width: $tablet-breakpoint) {
     max-width: 600px;
+    max-height: 80vh; // 桌面端保持80vh
   }
   
   // 深色模式适配
@@ -194,6 +197,36 @@ $animation-easing: cubic-bezier(0.25, 0.8, 0.25, 1);
   padding: 0 32px 32px 32px; // 与头部保持一致的左右内边距
   overflow-y: auto;
   flex: 1;
+  min-height: 0; // 确保flex子元素可以收缩
+  
+  // 自定义滚动条样式
+  &::-webkit-scrollbar {
+    width: 6px;
+  }
+  
+  &::-webkit-scrollbar-track {
+    background: transparent;
+  }
+  
+  &::-webkit-scrollbar-thumb {
+    background: rgba(0, 0, 0, 0.2);
+    border-radius: 3px;
+    
+    &:hover {
+      background: rgba(0, 0, 0, 0.3);
+    }
+  }
+  
+  // 深色模式滚动条
+  @media (prefers-color-scheme: dark) {
+    &::-webkit-scrollbar-thumb {
+      background: rgba(255, 255, 255, 0.3);
+      
+      &:hover {
+        background: rgba(255, 255, 255, 0.4);
+      }
+    }
+  }
   
   // 响应式调整
   @media (max-width: $mobile-breakpoint) {
@@ -633,6 +666,157 @@ $animation-easing: cubic-bezier(0.25, 0.8, 0.25, 1);
         }
       }
     }
+    
+    // 颜色描述区域
+    .color-description {
+      margin-top: 24px;
+      padding: 24px;
+      background: linear-gradient(135deg, #f8f9fa 0%, #ffffff 100%);
+      border: 1px solid #e5e5ea;
+      border-radius: 16px;
+      box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
+      
+      .description-header {
+        margin-bottom: 16px;
+        
+        h6 {
+          margin: 0 0 4px 0;
+          font-size: 18px;
+          font-weight: 700;
+          color: #1d1d1f;
+          display: flex;
+          align-items: center;
+          gap: 8px;
+          
+          &::before {
+            content: '🎨';
+            font-size: 20px;
+          }
+        }
+        
+        p {
+          margin: 0;
+          font-size: 14px;
+          color: #8e8e93;
+          font-weight: 400;
+        }
+      }
+      
+      .description-content {
+        background: #ffffff;
+        border: 2px solid #f0f0f0;
+        border-radius: 12px;
+        padding: 20px;
+        margin-bottom: 16px;
+        font-size: 15px;
+        line-height: 1.7;
+        color: #333333;
+        white-space: pre-line;
+        word-break: break-word;
+        max-height: 180px;
+        overflow-y: auto;
+        font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
+        transition: all 0.2s ease;
+        
+        &:hover {
+          border-color: #007aff;
+          box-shadow: 0 0 0 3px rgba(0, 122, 255, 0.1);
+        }
+        
+        &::-webkit-scrollbar {
+          width: 4px;
+        }
+        
+        &::-webkit-scrollbar-track {
+          background: transparent;
+        }
+        
+        &::-webkit-scrollbar-thumb {
+          background: rgba(0, 122, 255, 0.3);
+          border-radius: 2px;
+          
+          &:hover {
+            background: rgba(0, 122, 255, 0.5);
+          }
+        }
+        
+        &.empty {
+          color: #8e8e93;
+          font-style: italic;
+          text-align: center;
+          padding: 32px 20px;
+          background: #fafafa;
+          border-style: dashed;
+        }
+      }
+      
+      .description-actions {
+        display: flex;
+        align-items: center;
+        justify-content: space-between;
+        gap: 16px;
+        
+        @media (max-width: $mobile-breakpoint) {
+          flex-direction: column;
+          align-items: stretch;
+        }
+        
+        .copy-btn {
+          display: inline-flex;
+          align-items: center;
+          justify-content: center;
+          gap: 8px;
+          padding: 12px 20px;
+          background: linear-gradient(135deg, #007aff 0%, #0056cc 100%);
+          color: #ffffff;
+          border: none;
+          border-radius: 10px;
+          font-size: 14px;
+          font-weight: 600;
+          cursor: pointer;
+          transition: all 0.3s ease;
+          min-width: 100px;
+          box-shadow: 0 4px 12px rgba(0, 122, 255, 0.3);
+          
+          &:hover {
+            transform: translateY(-2px);
+            box-shadow: 0 6px 20px rgba(0, 122, 255, 0.4);
+            background: linear-gradient(135deg, #0056cc 0%, #003d99 100%);
+          }
+          
+          &:active {
+            transform: translateY(0);
+            box-shadow: 0 2px 8px rgba(0, 122, 255, 0.3);
+          }
+          
+          &.copied {
+            background: linear-gradient(135deg, #34c759 0%, #28a745 100%);
+            box-shadow: 0 4px 12px rgba(52, 199, 89, 0.3);
+            
+            &:hover {
+              background: linear-gradient(135deg, #28a745 0%, #1e7e34 100%);
+              box-shadow: 0 6px 20px rgba(52, 199, 89, 0.4);
+            }
+          }
+          
+          svg {
+            width: 14px;
+            height: 14px;
+          }
+        }
+        
+        .description-tip {
+          font-size: 12px;
+          color: #8e8e93;
+          flex: 1;
+          
+          @media (max-width: $mobile-breakpoint) {
+            text-align: center;
+            margin-top: 8px;
+          }
+        }
+      }
+    }
   }
   
   // 分析错误
@@ -979,6 +1163,69 @@ $animation-easing: cubic-bezier(0.25, 0.8, 0.25, 1);
             }
           }
         }
+        
+        .color-description {
+          background: linear-gradient(135deg, #2c2c2e 0%, #1c1c1e 100%);
+          border-color: #48484a;
+          box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
+          
+          .description-header {
+            h6 {
+              color: #f2f2f7;
+            }
+            
+            p {
+              color: #8e8e93;
+            }
+          }
+          
+          .description-content {
+            background: #1c1c1e;
+            border-color: #48484a;
+            color: #f2f2f7;
+            
+            &:hover {
+              border-color: #0a84ff;
+              box-shadow: 0 0 0 3px rgba(10, 132, 255, 0.2);
+            }
+            
+            &::-webkit-scrollbar-thumb {
+              background: rgba(10, 132, 255, 0.4);
+              
+              &:hover {
+                background: rgba(10, 132, 255, 0.6);
+              }
+            }
+            
+            &.empty {
+              background: #2c2c2e;
+              color: #8e8e93;
+              border-color: #48484a;
+            }
+          }
+          
+          .description-actions {
+            .copy-btn {
+              background: linear-gradient(135deg, #0a84ff 0%, #0056cc 100%);
+              box-shadow: 0 4px 12px rgba(10, 132, 255, 0.3);
+              
+              &:hover {
+                background: linear-gradient(135deg, #0056cc 0%, #003d99 100%);
+                box-shadow: 0 6px 20px rgba(10, 132, 255, 0.4);
+              }
+              
+              &.copied {
+                background: linear-gradient(135deg, #30d158 0%, #28a745 100%);
+                box-shadow: 0 4px 12px rgba(48, 209, 88, 0.3);
+                
+                &:hover {
+                  background: linear-gradient(135deg, #28a745 0%, #1e7e34 100%);
+                  box-shadow: 0 6px 20px rgba(48, 209, 88, 0.4);
+                }
+              }
+            }
+          }
+        }
       }
     }
     

+ 513 - 3
src/app/shared/components/upload-success-modal/upload-success-modal.component.ts

@@ -13,8 +13,15 @@ export interface UploadedFile {
   preview?: string;
 }
 
+export interface ColorInfo {
+  hex: string;
+  rgb: { r: number; g: number; b: number };
+  percentage: number;
+  name?: string; // 颜色名称,如"深蓝色"、"暖白色"等
+}
+
 export interface ColorAnalysisResult {
-  colors: Array<{ hex: string; rgb: { r: number; g: number; b: number }; percentage: number }>;
+  colors: ColorInfo[];
   reportUrl?: string;
   mosaicUrl?: string;
 }
@@ -50,6 +57,7 @@ export class UploadSuccessModalComponent implements OnInit, OnDestroy {
   // 动画状态
   animationState = 'idle';
   buttonHoverState = 'normal';
+  copySuccess = false; // 复制成功状态
 
   private progressSubscription?: Subscription;
   private resizeSubscription?: Subscription;
@@ -145,6 +153,21 @@ export class UploadSuccessModalComponent implements OnInit, OnDestroy {
 
   onViewReportClick() {
     if (this.analysisResult) {
+      // 创建一个新的窗口来显示完整报告
+      const reportWindow = window.open('', '_blank', 'width=1200,height=800,scrollbars=yes,resizable=yes');
+      
+      if (reportWindow) {
+        // 生成完整的HTML报告内容
+        const reportHtml = this.generateFullReport();
+        reportWindow.document.write(reportHtml);
+        reportWindow.document.close();
+        reportWindow.focus();
+      } else {
+        // 如果弹窗被阻止,则下载报告文件
+        this.downloadReport();
+      }
+      
+      // 触发事件给父组件
       this.viewReport.emit(this.analysisResult);
     }
   }
@@ -180,13 +203,500 @@ export class UploadSuccessModalComponent implements OnInit, OnDestroy {
     return this.uploadedFiles.some(file => file.type?.startsWith('image/'));
   }
 
-  private checkScreenSize() {
+  // 生成颜色描述文字
+  generateColorDescription(): string {
+    if (!this.analysisResult || !this.analysisResult.colors.length) {
+      return '';
+    }
+
+    const colorDescriptions = this.analysisResult.colors.map(color => {
+      const colorName = color.name || this.getColorName(color.hex);
+      return `${colorName}(${color.hex}) ${color.percentage}%`;
+    });
+
+    return `主要色彩:${colorDescriptions.join('、')}`;
+  }
+
+  // 根据色值获取颜色名称
+  private getColorName(hex: string): string {
+    const colorMap: { [key: string]: string } = {
+      '#FFFFFF': '纯白色',
+      '#F5F5F5': '白烟',
+      '#E5E5E5': '浅灰色',
+      '#CCCCCC': '中灰色',
+      '#999999': '深灰色',
+      '#666666': '暗灰色',
+      '#333333': '深暗灰',
+      '#000000': '纯黑色',
+      '#FF0000': '红色',
+      '#00FF00': '酸橙色',
+      '#0000FF': '蓝色',
+      '#FFFF00': '黄色',
+      '#FF00FF': '品红色',
+      '#00FFFF': '青色',
+      '#FFA500': '橙色',
+      '#800080': '紫色',
+      '#008000': '绿色',
+      '#000080': '海军蓝',
+      '#800000': '栗色',
+      '#808000': '橄榄色',
+      '#008080': '水鸭色',
+      '#C0C0C0': '银色',
+      '#808080': '灰色',
+      '#FFE4E1': '雾玫瑰',
+      '#F0F8FF': '爱丽丝蓝',
+      '#FAEBD7': '古董白',
+      '#F5F5DC': '米色',
+      '#DEB887': '硬木色',
+      '#A52A2A': '棕色',
+      '#D2691E': '巧克力色',
+      '#FF7F50': '珊瑚色',
+      '#6495ED': '矢车菊蓝',
+      '#DC143C': '深红色',
+      '#00008B': '深蓝色',
+      '#B8860B': '深金色',
+      '#A9A9A9': '深灰色',
+      '#006400': '深绿色',
+      '#BDB76B': '深卡其色',
+      '#8B008B': '深品红',
+      '#556B2F': '深橄榄绿',
+      '#FF8C00': '深橙色',
+      '#9932CC': '深兰花紫',
+      '#8B0000': '深红色2',
+      '#E9967A': '深鲑鱼色',
+      '#8FBC8F': '深海绿',
+      '#483D8B': '深石板蓝',
+      '#2F4F4F': '深石板灰',
+      '#00CED1': '深绿松石',
+      '#9400D3': '深紫罗兰',
+      '#FF1493': '深粉红',
+      '#00BFFF': '深天蓝',
+      '#696969': '暗灰色2',
+      '#1E90FF': '道奇蓝',
+      '#B22222': '火砖色',
+      '#FFFAF0': '花白色',
+      '#228B22': '森林绿',
+      '#DCDCDC': '淡灰色',
+      '#F8F8FF': '幽灵白',
+      '#FFD700': '金色',
+      '#DAA520': '金麒麟色',
+      '#ADFF2F': '绿黄色',
+      '#F0FFF0': '蜜瓜色',
+      '#FF69B4': '热粉红',
+      '#CD5C5C': '印度红',
+      '#4B0082': '靛青色',
+      '#FFFFF0': '象牙色',
+      '#F0E68C': '卡其色',
+      '#E6E6FA': '薰衣草色',
+      '#FFF0F5': '薰衣草红',
+      '#7CFC00': '草坪绿',
+      '#FFFACD': '柠檬绸',
+      '#ADD8E6': '浅蓝色',
+      '#F08080': '浅珊瑚色',
+      '#E0FFFF': '浅青色',
+      '#FAFAD2': '浅金菊黄',
+      '#D3D3D3': '浅灰色2',
+      '#90EE90': '浅绿色',
+      '#FFB6C1': '浅粉红',
+      '#FFA07A': '浅鲑鱼色',
+      '#20B2AA': '浅海绿',
+      '#87CEFA': '浅天蓝',
+      '#778899': '浅石板灰',
+      '#B0C4DE': '浅钢蓝',
+      '#FFFFE0': '浅黄色',
+      '#32CD32': '酸橙绿',
+      '#FAF0E6': '亚麻色',
+      '#66CDAA': '中海绿',
+      '#0000CD': '中蓝色',
+      '#BA55D3': '中兰花紫',
+      '#9370DB': '中紫色',
+      '#3CB371': '中海春绿',
+      '#7B68EE': '中石板蓝',
+      '#00FA9A': '中春绿',
+      '#48D1CC': '中绿松石',
+      '#C71585': '中紫罗兰红',
+      '#191970': '午夜蓝',
+      '#F5FFFA': '薄荷奶油',
+      '#FFDEAD': '那瓦霍白',
+      '#FDF5E6': '老花边',
+      '#6B8E23': '橄榄褐色',
+      '#FF4500': '橙红色',
+      '#DA70D6': '兰花紫',
+      '#EEE8AA': '灰秋麒麟',
+      '#98FB98': '灰绿色',
+      '#AFEEEE': '灰绿松石',
+      '#DB7093': '灰紫罗兰红',
+      '#FFEFD5': '番木瓜鞭',
+      '#FFDAB9': '桃扑',
+      '#CD853F': '秘鲁色',
+      '#FFC0CB': '粉红色',
+      '#DDA0DD': '洋李色',
+      '#B0E0E6': '粉蓝色',
+      '#BC8F8F': '玫瑰棕色',
+      '#4169E1': '皇家蓝',
+      '#8B4513': '马鞍棕色',
+      '#FA8072': '鲑鱼色',
+      '#F4A460': '沙棕色',
+      '#2E8B57': '海绿色',
+      '#FFF5EE': '海贝色',
+      '#A0522D': '赭色',
+      '#87CEEB': '天蓝色',
+      '#6A5ACD': '石板蓝',
+      '#708090': '石板灰',
+      '#FFFAFA': '雪色',
+      '#00FF7F': '春绿色',
+      '#4682B4': '钢蓝色',
+      '#D2B48C': '棕褐色',
+      '#D8BFD8': '蓟色',
+      '#FF6347': '番茄色',
+      '#40E0D0': '绿松石',
+      '#EE82EE': '紫罗兰',
+      '#F5DEB3': '小麦色',
+      '#9ACD32': '黄绿色'
+    };
+
+    // 如果找到精确匹配,返回对应名称
+    if (colorMap[hex.toUpperCase()]) {
+      return colorMap[hex.toUpperCase()];
+    }
+
+    // 否则根据RGB值判断颜色类型
+    const rgb = this.hexToRgb(hex);
+    if (!rgb) return '未知颜色';
+
+    const { r, g, b } = rgb;
+    const brightness = (r * 299 + g * 587 + b * 114) / 1000;
+
+    // 判断是否为灰色系
+    const isGray = Math.abs(r - g) < 30 && Math.abs(g - b) < 30 && Math.abs(r - b) < 30;
+    if (isGray) {
+      if (brightness > 240) return '浅灰白';
+      if (brightness > 200) return '浅灰色';
+      if (brightness > 160) return '中灰色';
+      if (brightness > 120) return '深灰色';
+      if (brightness > 80) return '暗灰色';
+      return '深暗灰';
+    }
+
+    // 判断主要颜色倾向
+    const max = Math.max(r, g, b);
+    const min = Math.min(r, g, b);
+    const saturation = max === 0 ? 0 : (max - min) / max;
+
+    if (saturation < 0.2) {
+      // 低饱和度,偏向灰色
+      if (brightness > 200) return '浅灰色';
+      if (brightness > 100) return '中灰色';
+      return '深灰色';
+    }
+
+    // 高饱和度,判断色相
+    let colorName = '';
+    if (r >= g && r >= b) {
+      if (g > b) {
+        colorName = brightness > 150 ? '浅橙色' : '橙色';
+      } else {
+        colorName = brightness > 150 ? '浅红色' : '红色';
+      }
+    } else if (g >= r && g >= b) {
+      if (r > b) {
+        colorName = brightness > 150 ? '浅黄绿' : '黄绿色';
+      } else {
+        colorName = brightness > 150 ? '浅绿色' : '绿色';
+      }
+    } else {
+      if (r > g) {
+        colorName = brightness > 150 ? '浅紫色' : '紫色';
+      } else {
+        colorName = brightness > 150 ? '浅蓝色' : '蓝色';
+      }
+    }
+
+    return colorName;
+  }
+
+  // 将十六进制颜色转换为RGB
+  private hexToRgb(hex: string): { r: number; g: number; b: number } | null {
+    const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
+    return result ? {
+      r: parseInt(result[1], 16),
+      g: parseInt(result[2], 16),
+      b: parseInt(result[3], 16)
+    } : null;
+  }
+
+  // 复制颜色描述到剪贴板
+  async copyColorDescription(): Promise<void> {
+    const description = this.generateColorDescription();
+    if (!description) return;
+
+    try {
+      await navigator.clipboard.writeText(description);
+      this.copySuccess = true;
+      console.log('颜色描述已复制到剪贴板');
+      
+      // 2秒后重置复制状态
+      setTimeout(() => {
+        this.copySuccess = false;
+      }, 2000);
+    } catch (err) {
+      console.error('复制失败:', err);
+      // 降级方案:使用传统方法
+      this.fallbackCopyTextToClipboard(description);
+    }
+  }
+
+  // 降级复制方案
+  private fallbackCopyTextToClipboard(text: string): void {
+    const textArea = document.createElement('textarea');
+    textArea.value = text;
+    textArea.style.top = '0';
+    textArea.style.left = '0';
+    textArea.style.position = 'fixed';
+    textArea.style.opacity = '0';
+
+    document.body.appendChild(textArea);
+    textArea.focus();
+    textArea.select();
+
+    try {
+      const successful = document.execCommand('copy');
+      if (successful) {
+        this.copySuccess = true;
+        console.log('颜色描述已复制到剪贴板(降级方案)');
+        
+        // 2秒后重置复制状态
+        setTimeout(() => {
+          this.copySuccess = false;
+        }, 2000);
+      }
+    } catch (err) {
+      console.error('降级复制方案也失败了:', err);
+    }
+
+    document.body.removeChild(textArea);
+  }
+
+  // 生成完整的HTML报告
+  private generateFullReport(): string {
+    if (!this.analysisResult) return '';
+    
+    const colors = this.analysisResult.colors;
+    const colorDescription = this.generateColorDescription();
+    
+    return `
+<!DOCTYPE html>
+<html lang="zh-CN">
+<head>
+  <meta charset="UTF-8">
+  <meta name="viewport" content="width=device-width, initial-scale=1.0">
+  <title>图片颜色分析完整报告</title>
+  <style>
+    body {
+      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
+      line-height: 1.6;
+      color: #333;
+      max-width: 1200px;
+      margin: 0 auto;
+      padding: 40px 20px;
+      background: #f8f9fa;
+    }
+    .report-container {
+      background: white;
+      border-radius: 16px;
+      padding: 40px;
+      box-shadow: 0 10px 40px rgba(0, 0, 0, 0.1);
+    }
+    h1 {
+      color: #1d1d1f;
+      font-size: 32px;
+      font-weight: 700;
+      margin-bottom: 8px;
+      text-align: center;
+    }
+    .subtitle {
+      color: #666;
+      font-size: 16px;
+      text-align: center;
+      margin-bottom: 40px;
+    }
+    .section {
+      margin-bottom: 40px;
+    }
+    .section-title {
+      color: #1d1d1f;
+      font-size: 24px;
+      font-weight: 600;
+      margin-bottom: 20px;
+      border-bottom: 2px solid #007AFF;
+      padding-bottom: 8px;
+    }
+    .color-grid {
+      display: grid;
+      grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
+      gap: 20px;
+      margin-bottom: 30px;
+    }
+    .color-item {
+      background: #f9f9f9;
+      border-radius: 12px;
+      padding: 20px;
+      text-align: center;
+      border: 1px solid #e5e5ea;
+      transition: transform 0.2s ease;
+    }
+    .color-item:hover {
+      transform: translateY(-2px);
+      box-shadow: 0 8px 25px rgba(0, 0, 0, 0.1);
+    }
+    .color-swatch {
+      width: 80px;
+      height: 80px;
+      border-radius: 50%;
+      margin: 0 auto 16px;
+      border: 3px solid white;
+      box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
+    }
+    .color-name {
+      font-weight: 600;
+      font-size: 16px;
+      color: #1d1d1f;
+      margin-bottom: 4px;
+    }
+    .color-hex {
+      font-family: 'Monaco', 'Menlo', monospace;
+      font-size: 14px;
+      color: #666;
+      margin-bottom: 4px;
+    }
+    .color-percentage {
+      font-size: 18px;
+      font-weight: 700;
+      color: #007AFF;
+    }
+    .description-section {
+      background: #f9f9f9;
+      border-radius: 12px;
+      padding: 24px;
+      border-left: 4px solid #007AFF;
+    }
+    .description-text {
+      font-size: 16px;
+      line-height: 1.8;
+      color: #333;
+      white-space: pre-wrap;
+    }
+    .stats-grid {
+      display: grid;
+      grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
+      gap: 16px;
+      margin-top: 30px;
+    }
+    .stat-item {
+      text-align: center;
+      padding: 16px;
+      background: #f0f8ff;
+      border-radius: 8px;
+    }
+    .stat-value {
+      font-size: 24px;
+      font-weight: 700;
+      color: #007AFF;
+    }
+    .stat-label {
+      font-size: 14px;
+      color: #666;
+      margin-top: 4px;
+    }
+    .footer {
+      text-align: center;
+      margin-top: 40px;
+      padding-top: 20px;
+      border-top: 1px solid #e5e5ea;
+      color: #666;
+      font-size: 14px;
+    }
+    @media print {
+      body { background: white; }
+      .report-container { box-shadow: none; }
+    }
+  </style>
+</head>
+<body>
+  <div class="report-container">
+    <h1>图片颜色分析报告</h1>
+    <p class="subtitle">基于AI智能分析生成的详细颜色报告</p>
+    
+    <div class="section">
+      <h2 class="section-title">颜色分析结果</h2>
+      <div class="color-grid">
+        ${colors.map(color => `
+          <div class="color-item">
+            <div class="color-swatch" style="background-color: ${color.hex}"></div>
+            <div class="color-name">${this.getColorName(color.hex)}</div>
+            <div class="color-hex">${color.hex}</div>
+            <div class="color-percentage">${color.percentage}%</div>
+          </div>
+        `).join('')}
+      </div>
+      
+      <div class="stats-grid">
+        <div class="stat-item">
+          <div class="stat-value">${colors.length}</div>
+          <div class="stat-label">主要颜色数量</div>
+        </div>
+        <div class="stat-item">
+          <div class="stat-value">${colors[0]?.percentage || 0}%</div>
+          <div class="stat-label">主色占比</div>
+        </div>
+        <div class="stat-item">
+          <div class="stat-value">${colors.reduce((sum, c) => sum + c.percentage, 0).toFixed(1)}%</div>
+          <div class="stat-label">总覆盖率</div>
+        </div>
+      </div>
+    </div>
+    
+    <div class="section">
+      <h2 class="section-title">颜色描述文字</h2>
+      <div class="description-section">
+        <div class="description-text">${colorDescription}</div>
+      </div>
+    </div>
+    
+    <div class="footer">
+      <p>报告生成时间:${new Date().toLocaleString('zh-CN')}</p>
+      <p>由颜色分析系统自动生成</p>
+    </div>
+  </div>
+</body>
+</html>`;
+  }
+
+  // 下载报告文件
+  private downloadReport(): void {
+    if (!this.analysisResult) return;
+    
+    const reportHtml = this.generateFullReport();
+    const blob = new Blob([reportHtml], { type: 'text/html;charset=utf-8' });
+    const url = URL.createObjectURL(blob);
+    
+    const link = document.createElement('a');
+    link.href = url;
+    link.download = `颜色分析报告_${new Date().toISOString().slice(0, 10)}.html`;
+    document.body.appendChild(link);
+    link.click();
+    document.body.removeChild(link);
+    
+    URL.revokeObjectURL(url);
+  }
+
+  private checkScreenSize(): void {
     const width = window.innerWidth;
     this.isMobile = width < 768;
     this.isTablet = width >= 768 && width < 1024;
   }
 
-  private setupResizeListener() {
+  private setupResizeListener(): void {
     // 可以添加更复杂的响应式逻辑
   }
 }