|  | @@ -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();
 |