ソースを参照

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

ryanemax 2 日 前
コミット
6dc80ab8e6

+ 12 - 5
src/app/pages/team-leader/dashboard/dashboard.html

@@ -379,15 +379,22 @@
                 {{ selectedEmployeeDetail.currentProjects }} 个
               </span>
             </div>
-            @if (selectedEmployeeDetail.projectNames.length > 0) {
+            @if (selectedEmployeeDetail.projectData.length > 0) {
               <div class="project-list">
                 <span class="project-label">核心项目:</span>
                 <div class="project-tags">
-                  @for (projectName of selectedEmployeeDetail.projectNames; track $index) {
-                    <span class="project-tag">{{ projectName }}</span>
+                  @for (project of selectedEmployeeDetail.projectData; track project.id) {
+                    <span class="project-tag clickable" 
+                          (click)="navigateToProjectFromPanel(project.id)"
+                          title="点击查看项目详情">
+                      {{ project.name }}
+                      <svg class="icon-arrow" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
+                        <path d="M7 17L17 7M17 7H7M17 7V17"/>
+                      </svg>
+                    </span>
                   }
-                  @if (selectedEmployeeDetail.currentProjects > selectedEmployeeDetail.projectNames.length) {
-                    <span class="project-tag more">+{{ selectedEmployeeDetail.currentProjects - selectedEmployeeDetail.projectNames.length }}</span>
+                  @if (selectedEmployeeDetail.currentProjects > selectedEmployeeDetail.projectData.length) {
+                    <span class="project-tag more">+{{ selectedEmployeeDetail.currentProjects - selectedEmployeeDetail.projectData.length }}</span>
                   }
                 </div>
               </div>

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

@@ -1231,9 +1231,45 @@
               border-radius: 16px;
               font-size: 12px;
               font-weight: 500;
+              transition: all 0.2s ease;
+              
+              &.clickable {
+                cursor: pointer;
+                display: inline-flex;
+                align-items: center;
+                gap: 4px;
+                background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+                box-shadow: 0 2px 4px rgba(102, 126, 234, 0.2);
+                
+                .icon-arrow {
+                  width: 14px;
+                  height: 14px;
+                  stroke-width: 2.5;
+                  opacity: 0;
+                  transform: translateX(-4px);
+                  transition: all 0.2s ease;
+                }
+                
+                &:hover {
+                  background: linear-gradient(135deg, #764ba2 0%, #667eea 100%);
+                  box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);
+                  transform: translateY(-2px);
+                  
+                  .icon-arrow {
+                    opacity: 1;
+                    transform: translateX(0);
+                  }
+                }
+                
+                &:active {
+                  transform: translateY(0);
+                  box-shadow: 0 2px 6px rgba(102, 126, 234, 0.3);
+                }
+              }
               
               &.more {
                 background: #94a3b8;
+                cursor: default;
               }
             }
           }

+ 302 - 56
src/app/pages/team-leader/dashboard/dashboard.ts

@@ -68,6 +68,7 @@ interface EmployeeDetail {
   name: string;
   currentProjects: number; // 当前负责项目数
   projectNames: string[]; // 项目名称列表(用于显示)
+  projectData: Array<{ id: string; name: string }>; // 项目数据(包含ID和名称,用于跳转)
   leaveRecords: LeaveRecord[]; // 未来7天请假记录
   redMarkExplanation: string; // 红色标记说明
 }
@@ -85,7 +86,6 @@ export class Dashboard implements OnInit, OnDestroy {
   projects: Project[] = [];
   filteredProjects: Project[] = [];
   todoTasks: TodoTask[] = [];
-  overdueProjects: Project[] = [];
   urgentPinnedProjects: Project[] = [];
   showAlert: boolean = false;
   selectedProjectId: string = '';
@@ -93,6 +93,9 @@ export class Dashboard implements OnInit, OnDestroy {
   // 真实设计师数据(从fmode-ng获取)
   realDesigners: any[] = [];
   
+  // 设计师工作量映射(从 ProjectTeam 表)
+  designerWorkloadMap: Map<string, any[]> = new Map(); // designerId/name -> projects[]
+  
   // 智能推荐相关
   showSmartMatch: boolean = false;
   selectedProject: any = null;
@@ -110,7 +113,6 @@ export class Dashboard implements OnInit, OnDestroy {
   private readonly MAX_SUGGESTIONS = 8; // 建议最大条数
   private isSearchFocused: boolean = false; // 是否处于输入聚焦态
   // 新增:临期项目与筛选状态
-  dueSoonProjects: Project[] = [];
   selectedType: 'all' | 'soft' | 'hard' = 'all';
   selectedUrgency: 'all' | 'high' | 'medium' | 'low' = 'all';
   selectedStatus: 'all' | 'progress' | 'completed' | 'overdue' | 'pendingApproval' | 'pendingAssignment' | 'dueSoon' = 'all';
@@ -228,9 +230,142 @@ export class Dashboard implements OnInit, OnDestroy {
         avgRating: d.tags.history.avgRating || 0,
         experience: 0 // 暂无此字段
       }));
-      console.log('✅ 加载设计师数据成功:', this.realDesigners.length, '人');
+      
+      // 加载设计师的实际工作量
+      await this.loadDesignerWorkload();
+    } catch (error) {
+      console.error('加载设计师数据失败:', error);
+    }
+  }
+  
+  /**
+   * 🔧 从 ProjectTeam 表加载每个设计师的实际工作量
+   */
+  async loadDesignerWorkload(): Promise<void> {
+    try {
+      const Parse = await import('fmode-ng/parse').then(m => m.FmodeParse.with('nova'));
+      
+      // 查询所有 ProjectTeam 记录
+      const cid = localStorage.getItem('company') || 'cDL6R1hgSi';
+      
+      // 先查询当前公司的所有项目
+      const projectQuery = new Parse.Query('Project');
+      projectQuery.equalTo('company', cid);
+      projectQuery.notEqualTo('isDeleted', true);
+      
+      // 查询当前公司项目的 ProjectTeam
+      const teamQuery = new Parse.Query('ProjectTeam');
+      teamQuery.matchesQuery('project', projectQuery);
+      teamQuery.notEqualTo('isDeleted', true);
+      teamQuery.include('project');
+      teamQuery.include('profile');
+      teamQuery.limit(1000);
+      
+      const teamRecords = await teamQuery.find();
+      
+      // 如果 ProjectTeam 表为空,使用降级方案
+      if (teamRecords.length === 0) {
+        await this.loadDesignerWorkloadFromProjects();
+        return;
+      }
+      
+      // 构建设计师工作量映射
+      this.designerWorkloadMap.clear();
+      
+      teamRecords.forEach((record: any) => {
+        const profile = record.get('profile');
+        const project = record.get('project');
+        
+        if (!profile || !project) {
+          return;
+        }
+        
+        const profileId = profile.id;
+        const profileName = profile.get('name') || profile.get('user')?.get?.('name') || `设计师-${profileId.slice(-4)}`;
+        
+        // 提取项目信息
+        const projectData = {
+          id: project.id,
+          name: project.get('title') || '未命名项目',
+          status: project.get('status') || '进行中',
+          currentStage: project.get('currentStage') || '未知阶段',
+          deadline: project.get('deadline'),
+          createdAt: project.get('createdAt'),
+          designerName: profileName // 设置为组员的名字
+        };
+        
+        // 添加到映射 (by ID)
+        if (!this.designerWorkloadMap.has(profileId)) {
+          this.designerWorkloadMap.set(profileId, []);
+        }
+        this.designerWorkloadMap.get(profileId)!.push(projectData);
+        
+        // 同时建立 name -> projects 的映射(用于甘特图)
+        if (!this.designerWorkloadMap.has(profileName)) {
+          this.designerWorkloadMap.set(profileName, []);
+        }
+        this.designerWorkloadMap.get(profileName)!.push(projectData);
+      });
+      
+    } catch (error) {
+      console.error('加载设计师工作量失败:', error);
+    }
+  }
+  
+  /**
+   * 🔧 降级方案:从 Project.assignee 统计工作量
+   * 当 ProjectTeam 表为空时使用
+   */
+  async loadDesignerWorkloadFromProjects(): Promise<void> {
+    try {
+      const Parse = await import('fmode-ng/parse').then(m => m.FmodeParse.with('nova'));
+      const cid = localStorage.getItem('company') || 'cDL6R1hgSi';
+      
+      // 查询所有项目
+      const projectQuery = new Parse.Query('Project');
+      projectQuery.equalTo('company', cid);
+      projectQuery.equalTo('isDeleted', false);
+      projectQuery.include('assignee');
+      projectQuery.include('department');
+      projectQuery.limit(1000);
+      
+      const projects = await projectQuery.find();
+      
+      // 构建设计师工作量映射
+      this.designerWorkloadMap.clear();
+      
+      projects.forEach((project: any) => {
+        const assignee = project.get('assignee');
+        if (!assignee) return;
+        
+        // 只统计组员角色的项目
+        const assigneeRole = assignee.get('roleName');
+        if (assigneeRole !== '组员') {
+          return;
+        }
+        
+        const assigneeName = assignee.get('name') || assignee.get('user')?.get?.('name') || `设计师-${assignee.id.slice(-4)}`;
+        
+        // 提取项目信息
+        const projectData = {
+          id: project.id,
+          name: project.get('title') || '未命名项目',
+          status: project.get('status') || '进行中',
+          currentStage: project.get('currentStage') || '未知阶段',
+          deadline: project.get('deadline'),
+          createdAt: project.get('createdAt'),
+          designerName: assigneeName
+        };
+        
+        // 添加到映射
+        if (!this.designerWorkloadMap.has(assigneeName)) {
+          this.designerWorkloadMap.set(assigneeName, []);
+        }
+        this.designerWorkloadMap.get(assigneeName)!.push(projectData);
+      });
+      
     } catch (error) {
-      console.error('❌ 加载设计师数据失败:', error);
+      console.error('[降级方案] 加载工作量失败:', error);
     }
   }
 
@@ -244,14 +379,12 @@ export class Dashboard implements OnInit, OnDestroy {
       // 如果有真实数据,使用真实数据
       if (realProjects && realProjects.length > 0) {
         this.projects = realProjects;
-        console.log('✅ 加载真实项目数据成功:', this.projects.length, '个项目');
       } else {
-        // 如果没有真实数据,使用模拟数据(便于开发测试)
-        console.warn('⚠️ 未找到真实项目数据,使用模拟数据');
+        // 如果没有真实数据,使用模拟数据
         this.projects = this.getMockProjects();
       }
     } catch (error) {
-      console.error('加载项目数据失败,使用模拟数据:', error);
+      console.error('加载项目数据失败:', error);
       this.projects = this.getMockProjects();
     }
     
@@ -537,15 +670,13 @@ export class Dashboard implements OnInit, OnDestroy {
       return { ...p, deadline, createdAt } as Project;
     });
 
-    // 筛选超期与临期项目
-    this.overdueProjects = this.projects.filter(project => project.isOverdue);
-    this.dueSoonProjects = this.projects.filter(project => project.dueSoon && !project.isOverdue);
+    // 筛选结果初始化为全部项目
     this.filteredProjects = [...this.projects];
 
     // 供筛选用的设计师列表
     this.designers = Array.from(new Set(this.projects.map(p => p.designerName).filter(n => !!n)));
 
-    // 显示超期提醒
+    // 显示超期提醒(使用 getter)
     if (this.overdueProjects.length > 0) {
       this.showAlert = true;
     }
@@ -946,9 +1077,8 @@ export class Dashboard implements OnInit, OnDestroy {
     
     try {
       this.recommendations = await this.designerService.getRecommendedDesigners(project, this.realDesigners);
-      console.log('✅ 智能推荐结果:', this.recommendations);
     } catch (error) {
-      console.error('智能推荐失败:', error);
+      console.error('智能推荐失败:', error);
       this.recommendations = [];
     }
   }
@@ -1848,9 +1978,9 @@ export class Dashboard implements OnInit, OnDestroy {
     if (this.workloadGanttScale === 'week') {
       // 周视图:显示未来7天
       xMin = todayTs;
-      xMax = todayTs + 7 * DAY - 1;
+      xMax = todayTs + 7 * DAY;
       xSplitNumber = 7;
-      xLabelFormatter = (val) => {
+      xLabelFormatter = (val: any) => {
         const date = new Date(val);
         const weekDays = ['周日', '周一', '周二', '周三', '周四', '周五', '周六'];
         return `${date.getMonth() + 1}/${date.getDate()}\n${weekDays[date.getDay()]}`;
@@ -1858,26 +1988,23 @@ export class Dashboard implements OnInit, OnDestroy {
     } else {
       // 月视图:显示未来30天
       xMin = todayTs;
-      xMax = todayTs + 30 * DAY - 1;
+      xMax = todayTs + 30 * DAY;
       xSplitNumber = 30;
-      xLabelFormatter = (val) => {
+      xLabelFormatter = (val: any) => {
         const date = new Date(val);
         return `${date.getMonth() + 1}/${date.getDate()}`;
       };
     }
 
-    // 获取所有真实设计师(优先使用realDesigners)
+    // 获取所有真实设计师
     let designers: string[] = [];
     
     if (this.realDesigners && this.realDesigners.length > 0) {
-      // 使用真实的设计师列表
       designers = this.realDesigners.map(d => d.name);
-      console.log('✅ 使用真实设计师列表:', designers.length, '人');
     } else {
-      // 降级:从已分配的项目中提取设计师(过滤掉"未分配")
+      // 降级:从已分配的项目中提取设计师
       const assigned = this.filteredProjects.filter(p => p.designerName && p.designerName !== '未分配');
       designers = Array.from(new Set(assigned.map(p => p.designerName)));
-      console.warn('⚠️ 使用项目中提取的设计师列表:', designers.length, '人');
     }
     
     if (designers.length === 0) {
@@ -1896,10 +2023,7 @@ export class Dashboard implements OnInit, OnDestroy {
       return;
     }
     
-    // 获取所有已分配的项目(过滤掉"未分配")
-    const assigned = this.filteredProjects.filter(p => p.designerName && p.designerName !== '未分配');
-    
-    // 计算每个设计师的每日工作状态
+    // 🔧 使用 ProjectTeam 表的数据(实际执行人)
     const workloadByDesigner: Record<string, any[]> = {};
     designers.forEach(name => {
       workloadByDesigner[name] = [];
@@ -1908,7 +2032,7 @@ export class Dashboard implements OnInit, OnDestroy {
     // 计算每个设计师的总负载(用于排序)
     const designerTotalLoad: Record<string, number> = {};
     designers.forEach(name => {
-      const projects = assigned.filter(p => p.designerName === name);
+      const projects = this.designerWorkloadMap.get(name) || [];
       designerTotalLoad[name] = projects.length;
     });
     
@@ -1919,7 +2043,7 @@ export class Dashboard implements OnInit, OnDestroy {
     
     // 为每个设计师生成时间段数据
     sortedDesigners.forEach((designerName, yIndex) => {
-      const designerProjects = assigned.filter(p => p.designerName === designerName);
+      const designerProjects = this.designerWorkloadMap.get(designerName) || [];
       
       // 计算每一天的状态
       const days = this.workloadGanttScale === 'week' ? 7 : 30;
@@ -1929,8 +2053,31 @@ export class Dashboard implements OnInit, OnDestroy {
         
         // 查找该天有哪些项目
         const dayProjects = designerProjects.filter(p => {
-          const pStart = p.createdAt ? new Date(p.createdAt).getTime() : dayStart;
+          // 如果项目没有 deadline,则认为项目一直在进行中
+          if (!p.deadline) {
+            return true; // 没有截止日期的项目始终显示
+          }
+          
           const pEnd = new Date(p.deadline).getTime();
+          
+          // 检查时间是否有效
+          if (isNaN(pEnd)) {
+            return true; // 如果截止日期无效,认为项目在进行中
+          }
+          
+          // 🔧 修复:对于进行中的项目(状态不是"已完成"),即使过期也显示
+          // 这样可以在甘特图中看到超期的项目
+          const isCompleted = p.status === '已完成' || p.status === '已交付';
+          if (!isCompleted) {
+            // 进行中的项目:只要截止日期还没到很久之前(比如30天前),就显示
+            const thirtyDaysAgo = todayTs - 30 * DAY;
+            if (pEnd >= thirtyDaysAgo) {
+              return true; // 30天内的项目都显示
+            }
+          }
+          
+          // 已完成的项目:正常时间范围判断
+          const pStart = p.createdAt ? new Date(p.createdAt).getTime() : dayStart;
           return !(pEnd < dayStart || pStart > dayEnd);
         });
 
@@ -2043,17 +2190,29 @@ export class Dashboard implements OnInit, OnDestroy {
         bottom: 60
       },
       xAxis: {
-        type: 'value',
+        type: 'time',
         min: xMin,
         max: xMax,
-        splitNumber: xSplitNumber,
+        boundaryGap: false,
         axisLine: { lineStyle: { color: '#e5e7eb' } },
         axisLabel: {
           color: '#6b7280',
           formatter: xLabelFormatter,
-          interval: this.workloadGanttScale === 'week' ? 0 : 4
+          interval: 0,
+          rotate: this.workloadGanttScale === 'week' ? 0 : 45,
+          showMinLabel: true,
+          showMaxLabel: true
         },
-        splitLine: { lineStyle: { color: '#f1f5f9' } }
+        axisTick: {
+          alignWithLabel: true,
+          interval: 0
+        },
+        splitLine: { 
+          show: true,
+          lineStyle: { color: '#f1f5f9' }
+        },
+        splitNumber: xSplitNumber,
+        minInterval: DAY
       },
       yAxis: {
         type: 'category',
@@ -2143,14 +2302,65 @@ export class Dashboard implements OnInit, OnDestroy {
 
   // 新增:阶段到核心阶段的映射
   private mapStageToCorePhase(stageId: string): 'order' | 'requirements' | 'delivery' | 'aftercare' {
-    // 订单分配:立项初期
-    if (stageId === 'pendingApproval' || stageId === 'pendingAssignment') return 'order';
-    // 确认需求:需求沟通 + 方案规划
-    if (stageId === 'requirement' || stageId === 'planning') return 'requirements';
-    // 交付执行:制作与评审修订过程
-    if (stageId === 'modeling' || stageId === 'rendering' || stageId === 'postProduction' || stageId === 'review' || stageId === 'revision') return 'delivery';
-    // 售后:交付完成后的跟进(当前数据以交付完成代表进入售后)
-    return 'aftercare';
+    if (!stageId) return 'order'; // 空值默认为订单分配
+    
+    // 标准化阶段名称(去除空格,转小写)
+    const normalizedStage = stageId.trim().toLowerCase();
+    
+    // 1. 订单分配阶段(英文ID + 中文名称)
+    if (normalizedStage === 'order' || 
+        normalizedStage === 'pendingapproval' || 
+        normalizedStage === 'pendingassignment' ||
+        normalizedStage === '订单分配' ||
+        normalizedStage === '待审批' ||
+        normalizedStage === '待分配') {
+      return 'order';
+    }
+    
+    // 2. 确认需求阶段(英文ID + 中文名称)
+    if (normalizedStage === 'requirements' ||
+        normalizedStage === 'requirement' || 
+        normalizedStage === 'planning' ||
+        normalizedStage === '确认需求' ||
+        normalizedStage === '需求沟通' ||
+        normalizedStage === '方案规划') {
+      return 'requirements';
+    }
+    
+    // 3. 交付执行阶段(英文ID + 中文名称)
+    if (normalizedStage === 'delivery' ||
+        normalizedStage === 'modeling' || 
+        normalizedStage === 'rendering' || 
+        normalizedStage === 'postproduction' || 
+        normalizedStage === 'review' || 
+        normalizedStage === 'revision' ||
+        normalizedStage === '交付执行' ||
+        normalizedStage === '建模' ||
+        normalizedStage === '建模阶段' ||
+        normalizedStage === '渲染' ||
+        normalizedStage === '渲染阶段' ||
+        normalizedStage === '后期制作' ||
+        normalizedStage === '评审' ||
+        normalizedStage === '修改' ||
+        normalizedStage === '修订') {
+      return 'delivery';
+    }
+    
+    // 4. 售后归档阶段(英文ID + 中文名称)
+    if (normalizedStage === 'aftercare' ||
+        normalizedStage === 'completed' ||
+        normalizedStage === 'archived' ||
+        normalizedStage === '售后归档' ||
+        normalizedStage === '售后' ||
+        normalizedStage === '归档' ||
+        normalizedStage === '已完成' ||
+        normalizedStage === '已交付') {
+      return 'aftercare';
+    }
+    
+    // 未匹配的阶段:默认为交付执行(因为大部分时间项目都在执行中)
+    console.warn(`⚠️ 未识别的阶段: "${stageId}" → 默认归类为交付执行`);
+    return 'delivery';
   }
 
   // 新增:获取核心阶段的项目
@@ -2168,16 +2378,34 @@ export class Dashboard implements OnInit, OnDestroy {
     return this.getProjectsByStage(stageId).length;
   }
 
-  // 待审批项目:currentStage === 'pendingApproval'
+  // 🔥 已延期项目
+  get overdueProjects(): Project[] {
+    return this.projects.filter(p => p.isOverdue);
+  }
+
+  // ⏳ 临期项目(3天内)
+  get dueSoonProjects(): Project[] {
+    return this.projects.filter(p => p.dueSoon && !p.isOverdue);
+  }
+
+  // 📋 待审批项目(支持中文和英文阶段名称)
   get pendingApprovalProjects(): Project[] {
-    const src = (this.filteredProjects && this.filteredProjects.length) ? this.filteredProjects : this.projects;
-    return src.filter(p => p.currentStage === 'pendingApproval');
+    return this.projects.filter(p => {
+      const stage = (p.currentStage || '').trim().toLowerCase();
+      return stage === 'pendingapproval' || 
+             stage === '待审批' || 
+             stage === '待确认';
+    });
   }
 
-  // 待指派项目:currentStage === 'pendingAssignment'
+  // 🎯 待分配项目(支持中文和英文阶段名称)
   get pendingAssignmentProjects(): Project[] {
-    const src = (this.filteredProjects && this.filteredProjects.length) ? this.filteredProjects : this.projects;
-    return src.filter(p => p.currentStage === 'pendingAssignment');
+    return this.projects.filter(p => {
+      const stage = (p.currentStage || '').trim().toLowerCase();
+      return stage === 'pendingassignment' || 
+             stage === '待分配' ||
+             stage === '订单分配';
+    });
   }
 
   // 智能推荐设计师
@@ -2219,7 +2447,6 @@ export class Dashboard implements OnInit, OnDestroy {
   // 查看项目详情 - 跳转到纯净的项目详情页(无管理端UI)
   viewProjectDetails(projectId: string): void {
     if (!projectId) {
-      console.warn('⚠️ 项目ID为空,无法跳转');
       return;
     }
     
@@ -2227,10 +2454,7 @@ export class Dashboard implements OnInit, OnDestroy {
     const cid = localStorage.getItem('company') || 'cDL6R1hgSi';
     
     // 跳转到wxwork路由的项目详情页(纯净页面,无管理端侧边栏)
-    // 路由格式:/wxwork/:cid/project/:projectId/order
     this.router.navigate(['/wxwork', cid, 'project', projectId, 'order']);
-    
-    console.log('✅ 组长端跳转到纯净项目详情页:', projectId);
   }
 
   // 快速分配项目(增强:加入智能推荐)
@@ -2346,10 +2570,17 @@ export class Dashboard implements OnInit, OnDestroy {
 
   // 生成员工详情数据
   private generateEmployeeDetail(employeeName: string): EmployeeDetail {
-    // 获取该员工负责的项目
-    const employeeProjects = this.filteredProjects.filter(p => p.designerName === employeeName);
+    // 🔧 从 ProjectTeam 表获取该员工负责的项目
+    const employeeProjects = this.designerWorkloadMap.get(employeeName) || [];
     const currentProjects = employeeProjects.length;
-    const projectNames = employeeProjects.slice(0, 3).map(p => p.name); // 最多显示3个项目名称
+    
+    // 保存完整的项目数据(最多显示3个)
+    const projectData = employeeProjects.slice(0, 3).map(p => ({
+      id: p.id,
+      name: p.name
+    }));
+    
+    const projectNames = projectData.map(p => p.name); // 项目名称列表
     
     // 获取该员工的请假记录(未来7天)
     const today = new Date();
@@ -2370,6 +2601,7 @@ export class Dashboard implements OnInit, OnDestroy {
       name: employeeName,
       currentProjects,
       projectNames,
+      projectData,
       leaveRecords: employeeLeaveRecords,
       redMarkExplanation
     };
@@ -2409,6 +2641,20 @@ export class Dashboard implements OnInit, OnDestroy {
     this.selectedEmployeeDetail = null;
   }
 
+  // 从员工详情面板跳转到项目详情
+  navigateToProjectFromPanel(projectId: string): void {
+    if (!projectId) {
+      return;
+    }
+    
+    // 关闭员工详情面板
+    this.closeEmployeeDetailPanel();
+    
+    // 跳转到项目详情页(使用纯净的wxwork路由)
+    const cid = localStorage.getItem('company') || 'cDL6R1hgSi';
+    this.router.navigate(['/wxwork', cid, 'project', projectId, 'order']);
+  }
+
   // 获取请假类型显示文本
   getLeaveTypeText(leaveType?: string): string {
     const typeMap: Record<string, string> = {
@@ -2454,7 +2700,7 @@ export class Dashboard implements OnInit, OnDestroy {
       });
 
       // 检查项目繁忙情况,如果项目数>=3,也添加红色标记
-      const employeeProjects = this.filteredProjects.filter(p => p.designerName === employeeName);
+      const employeeProjects = this.designerWorkloadMap.get(employeeName) || [];
       if (employeeProjects.length >= 3) {
         // 在当前日期添加繁忙标记
         const today = new Date();

+ 3 - 21
src/app/pages/team-leader/services/designer.service.ts

@@ -35,7 +35,6 @@ export class DesignerService {
   
   constructor() {
     this.cid = localStorage.getItem('company') || '';
-    console.log('🏢 DesignerService初始化,当前公司ID:', this.cid || '(未设置)');
     this.initParse();
   }
   
@@ -46,9 +45,8 @@ export class DesignerService {
     try {
       const { FmodeParse } = await import('fmode-ng/parse');
       this.Parse = FmodeParse.with("nova");
-      console.log('✅ DesignerService: FmodeParse 初始化成功');
     } catch (error) {
-      console.error('DesignerService: FmodeParse 初始化失败:', error);
+      console.error('DesignerService: FmodeParse 初始化失败:', error);
     }
   }
   
@@ -81,8 +79,6 @@ export class DesignerService {
       
       const profiles = await query.find();
       
-      console.log(`✅ 获取到 ${profiles.length} 个设计师(组员)`);
-      
       return profiles.map((p: any, index: number) => {
         const data = p.get('data') || {};
         const tags = data.tags || this.getDefaultTags();
@@ -114,11 +110,6 @@ export class DesignerService {
           name = `设计师-${p.id.slice(-4)}`;
         }
         
-        // 如果使用了备用名称,发出警告
-        if (!p.get('name')) {
-          console.warn(`⚠️ Profile ${p.id} 缺少 name 字段,使用备用显示: ${name}`);
-        }
-        
         return {
           id: p.id,
           name: name,
@@ -170,10 +161,9 @@ export class DesignerService {
       
       await profile.save();
       
-      console.log('✅ 设计师tags更新成功:', designerId);
       return true;
     } catch (error) {
-      console.error('更新设计师tags失败:', error);
+      console.error('更新设计师tags失败:', error);
       return false;
     }
   }
@@ -285,7 +275,6 @@ export class DesignerService {
     }
     
     try {
-      console.log('🔍 开始查询项目,company:', this.cid);
       const query = new Parse.Query('Project');
       query.equalTo('company', this.cid);
       query.notEqualTo('isDeleted', true);
@@ -294,12 +283,6 @@ export class DesignerService {
       query.limit(1000);
       
       const projects = await query.find();
-      console.log(`✅ Parse查询成功,找到 ${projects.length} 个项目`);
-      
-      if (projects.length === 0) {
-        console.warn('⚠️ 数据库中没有符合条件的项目数据');
-        console.warn('💡 提示:请确保Project表中有数据,且company字段=', this.cid);
-      }
       
       return projects.map((p: any) => this.transformProject(p));
     } catch (error) {
@@ -510,10 +493,9 @@ export class DesignerService {
       project.set('status', '进行中');
       
       await project.save();
-      console.log('✅ 项目分配成功');
       return true;
     } catch (error) {
-      console.error('项目分配失败:', error);
+      console.error('项目分配失败:', error);
       return false;
     }
   }