Prechádzať zdrojové kódy

fix: improve project timeline and dashboard logic for better date handling and visibility

- Enhanced the dashboard to convert project data into a timeline format after loading, improving data representation.
- Updated date handling logic to use a more reasonable default for projects without deadlines, extending the duration from 7 to 30 days.
- Implemented checks for projects with past end dates, ensuring they are displayed correctly on the timeline.
- Improved filtering logic for completed projects, ensuring only active projects are shown in the timeline and dashboard.
- Added detailed logging for debugging and visibility into project status and date calculations, aiding in future troubleshooting.
0235711 3 dní pred
rodič
commit
1220a74b70

+ 63 - 3
src/app/pages/team-leader/dashboard/dashboard.ts

@@ -533,6 +533,10 @@ export class Dashboard implements OnInit, OnDestroy {
         this.designerWorkloadMap.get(assigneeName)!.push(projectData);
       });
       
+      // ✅ 修复:加载完数据后,转换为时间轴格式
+      console.log(`📊 [降级方案] 加载了 ${projects.length} 个项目,填充到 ${this.designerWorkloadMap.size} 个设计师的工作量映射`);
+      this.convertToProjectTimeline();
+      
     } catch (error) {
       console.error('[降级方案] 加载工作量失败:', error);
     }
@@ -571,8 +575,11 @@ export class Dashboard implements OnInit, OnDestroy {
       currentSize += projects.length;
     });
     
+    console.log(`📊 convertToProjectTimeline: 当前数据大小 = ${currentSize}, 缓存大小 = ${this.lastDesignerWorkloadMapSize}`);
+    
     // 如果数据没有变化,使用缓存
     if (currentSize === this.lastDesignerWorkloadMapSize && this.timelineDataCache.length > 0) {
+      console.log(`📊 使用缓存数据,共 ${this.timelineDataCache.length} 个项目`);
       this.projectTimelineData = this.timelineDataCache;
       return;
     }
@@ -582,10 +589,13 @@ export class Dashboard implements OnInit, OnDestroy {
     
     // 统计项目数量
     let totalProjectsInMap = 0;
-    this.designerWorkloadMap.forEach((projects) => {
+    this.designerWorkloadMap.forEach((projects, key) => {
       totalProjectsInMap += projects.length;
+      console.log(`📊 设计师 "${key}": ${projects.length} 个项目`);
     });
     
+    console.log(`📊 总计 ${totalProjectsInMap} 个项目分布在 ${this.designerWorkloadMap.size} 个设计师中`);
+    
     this.designerWorkloadMap.forEach((projects, designerKey) => {
       // 🔧 改进判断逻辑:跳过明显的 ID 格式(Parse objectId 是10位字母数字组合)
       // 只要包含中文字符,就认为是设计师名称
@@ -607,6 +617,8 @@ export class Dashboard implements OnInit, OnDestroy {
       }
     });
     
+    console.log(`📊 开始转换 ${allDesignerProjects.length} 个项目为时间轴格式`);
+    
     this.projectTimelineData = allDesignerProjects.map((project, index) => {
       const now = new Date();
       
@@ -634,8 +646,21 @@ export class Dashboard implements OnInit, OnDestroy {
       if (project.deadline) {
         realEndDate = project.deadline instanceof Date ? project.deadline : new Date(project.deadline);
       } else {
-        // 降级:如果没有交付日期,使用开始时间 + 7天
-        realEndDate = new Date(realStartDate.getTime() + 7 * 24 * 60 * 60 * 1000);
+        // ✅ 修复:如果没有交付日期,使用开始时间 + 30天(更合理的默认值)
+        // 避免新项目因为默认7天导致结束日期在过去而被过滤
+        realEndDate = new Date(realStartDate.getTime() + 30 * 24 * 60 * 60 * 1000);
+      }
+      
+      // ✅ 调试:检查新项目
+      if (project.id === 'qCV9QHROSH') {
+        console.log(`📊 新项目 qCV9QHROSH 日期计算:`, {
+          projectName: project.name,
+          hasDeadline: !!project.deadline,
+          deadline: project.deadline ? (project.deadline instanceof Date ? project.deadline.toLocaleString('zh-CN') : new Date(project.deadline).toLocaleString('zh-CN')) : '无',
+          realStartDate: realStartDate.toLocaleString('zh-CN'),
+          realEndDate: realEndDate.toLocaleString('zh-CN'),
+          calculatedEndDate: realEndDate.toLocaleString('zh-CN')
+        });
       }
       
       // 3. 获取真实的对图时间
@@ -868,6 +893,41 @@ export class Dashboard implements OnInit, OnDestroy {
     // 更新缓存
     this.timelineDataCache = this.projectTimelineData;
     this.lastDesignerWorkloadMapSize = currentSize;
+    
+    console.log(`📊 convertToProjectTimeline 完成: 转换了 ${this.projectTimelineData.length} 个项目`);
+    if (this.projectTimelineData.length > 0) {
+      const now = new Date();
+      const sampleProject = this.projectTimelineData[0];
+      const daysFromStart = Math.floor((now.getTime() - sampleProject.startDate.getTime()) / (1000 * 60 * 60 * 24));
+      const daysToEnd = Math.floor((sampleProject.endDate.getTime() - now.getTime()) / (1000 * 60 * 60 * 24));
+      
+      console.log(`📊 第一个项目示例:`, {
+        projectId: sampleProject.projectId,
+        projectName: sampleProject.projectName,
+        designerName: sampleProject.designerName,
+        startDate: sampleProject.startDate.toLocaleString('zh-CN'),
+        endDate: sampleProject.endDate.toLocaleString('zh-CN'),
+        startDateFromNow: `${daysFromStart} 天前`,
+        endDateFromNow: daysToEnd >= 0 ? `${daysToEnd} 天后` : `${Math.abs(daysToEnd)} 天前`,
+        isEndDateInPast: daysToEnd < 0,
+        hasPhaseDeadlines: !!sampleProject.phaseDeadlines
+      });
+      
+      // ✅ 检查日期问题:统计有多少项目的结束日期在过去
+      const projectsWithPastEndDate = this.projectTimelineData.filter(p => {
+        const daysToEnd = Math.floor((p.endDate.getTime() - now.getTime()) / (1000 * 60 * 60 * 24));
+        return daysToEnd < 0;
+      });
+      
+      if (projectsWithPastEndDate.length > 0) {
+        console.warn(`⚠️ 发现 ${projectsWithPastEndDate.length} 个项目的结束日期在过去,这些项目在时间轴上可能显示为一个点`);
+        console.log(`⚠️ 示例项目:`, projectsWithPastEndDate.slice(0, 3).map(p => ({
+          name: p.projectName,
+          endDate: p.endDate.toLocaleString('zh-CN'),
+          daysAgo: Math.abs(Math.floor((p.endDate.getTime() - now.getTime()) / (1000 * 60 * 60 * 24)))
+        })));
+      }
+    }
   }
   
   /**

+ 90 - 2
src/app/pages/team-leader/project-timeline/project-timeline.ts

@@ -162,15 +162,43 @@ export class ProjectTimelineComponent implements OnInit, OnDestroy {
   
   /**
    * 获取项目在时间轴上的位置和宽度
+   * ✅ 修复:处理项目结束日期在过去的情况
    */
   getProjectPosition(project: ProjectTimeline): { left: string; width: string; background: string } {
     const rangeStart = this.timeRangeStart.getTime();
     const rangeEnd = this.timeRangeEnd.getTime();
     const rangeDuration = rangeEnd - rangeStart;
     
+    // ✅ 修复:如果项目完全在过去,显示一个最小宽度的条
+    if (project.endDate.getTime() < rangeStart) {
+      // 项目完全在过去,显示在时间轴最左侧,宽度为最小可见宽度
+      return {
+        left: '0%',
+        width: '2px', // 最小可见宽度
+        background: this.getProjectUrgencyColor(project)
+      };
+    }
+    
+    // ✅ 修复:如果项目开始日期在过去,但结束日期在未来,从时间轴开始位置显示
     const projectStart = Math.max(project.startDate.getTime(), rangeStart);
     const projectEnd = Math.min(project.endDate.getTime(), rangeEnd);
     
+    // ✅ 修复:确保 projectEnd >= projectStart
+    if (projectEnd < projectStart) {
+      // 这种情况不应该发生,但如果发生了,显示最小宽度
+      console.warn(`⚠️ 项目 ${project.projectName} 的结束日期早于开始日期或时间范围开始`, {
+        startDate: project.startDate.toLocaleString('zh-CN'),
+        endDate: project.endDate.toLocaleString('zh-CN'),
+        rangeStart: new Date(rangeStart).toLocaleString('zh-CN'),
+        rangeEnd: new Date(rangeEnd).toLocaleString('zh-CN')
+      });
+      return {
+        left: '0%',
+        width: '2px',
+        background: this.getProjectUrgencyColor(project)
+      };
+    }
+    
     const left = ((projectStart - rangeStart) / rangeDuration) * 100;
     const width = ((projectEnd - projectStart) / rangeDuration) * 100;
     
@@ -179,7 +207,7 @@ export class ProjectTimelineComponent implements OnInit, OnDestroy {
     
     return {
       left: `${Math.max(0, left)}%`,
-      width: `${Math.max(1, Math.min(100 - left, width))}%`,
+      width: `${Math.max(2, Math.min(100 - left, width))}%`, // ✅ 修复:最小宽度改为2px(约2%)
       background
     };
   }
@@ -481,8 +509,19 @@ export class ProjectTimelineComponent implements OnInit, OnDestroy {
   private buildDesignerStats(): DesignerInfo[] {
     const designerMap = new Map<string, DesignerInfo>();
     
+    // ✅ 过滤已完成的项目后再统计
+    const now = this.currentTime.getTime();
+    const activeProjects = this.projects.filter(project => {
+      // ✅ 修复:使用相同的过滤逻辑
+      const endDatePast = project.endDate.getTime() < now;
+      const daysPast = endDatePast ? Math.floor((now - project.endDate.getTime()) / (1000 * 60 * 60 * 24)) : 0;
+      const isCompleted = (project.currentStage === 'delivery' && endDatePast) || 
+                          (endDatePast && daysPast > 7);
+      return !isCompleted;
+    });
+    
     // 🔧 统计每个设计师负责的项目(不去重,因为项目列表已经包含了所有设计师-项目关联)
-    this.projects.forEach(project => {
+    activeProjects.forEach(project => {
       const designerName = project.designerName || '未分配';
       
       if (!designerMap.has(designerName)) {
@@ -525,6 +564,55 @@ export class ProjectTimelineComponent implements OnInit, OnDestroy {
   applyFilters(): void {
     let result = [...this.projects];
     
+    // ✅ 过滤已完成的项目
+    const now = this.currentTime.getTime();
+    const filteredOut: any[] = [];
+    
+    result = result.filter(project => {
+      // 判断项目是否已完成:
+      // 1. currentStage 为 'delivery'(交付阶段)且 endDate 在过去
+      // 2. 或者 endDate 在过去超过7天(给一个缓冲期)
+      const endDatePast = project.endDate.getTime() < now;
+      const daysPast = endDatePast ? Math.floor((now - project.endDate.getTime()) / (1000 * 60 * 60 * 24)) : 0;
+      const isCompleted = (project.currentStage === 'delivery' && endDatePast) || 
+                          (endDatePast && daysPast > 7); // ✅ 修复:只过滤结束日期在过去超过7天的项目
+      
+      if (isCompleted) {
+        filteredOut.push({
+          projectId: project.projectId,
+          projectName: project.projectName,
+          currentStage: project.currentStage,
+          endDate: project.endDate.toLocaleString('zh-CN'),
+          daysPast: daysPast
+        });
+      }
+      
+      return !isCompleted; // 只保留未完成的项目
+    });
+    
+    console.log(`📊 过滤已完成项目后剩余: ${result.length} 个项目(原始: ${this.projects.length})`);
+    if (filteredOut.length > 0) {
+      console.log(`📊 已过滤的项目(${filteredOut.length}个):`, filteredOut.slice(0, 5));
+    }
+    
+    // ✅ 调试:检查特定项目
+    const targetProject = this.projects.find(p => p.projectId === 'qCV9QHROSH');
+    if (targetProject) {
+      const endDatePast = targetProject.endDate.getTime() < now;
+      const daysPast = endDatePast ? Math.floor((now - targetProject.endDate.getTime()) / (1000 * 60 * 60 * 24)) : 0;
+      console.log(`📊 新项目 qCV9QHROSH 详情:`, {
+        projectName: targetProject.projectName,
+        currentStage: targetProject.currentStage,
+        startDate: targetProject.startDate.toLocaleString('zh-CN'),
+        endDate: targetProject.endDate.toLocaleString('zh-CN'),
+        endDatePast: endDatePast,
+        daysPast: daysPast,
+        isFiltered: filteredOut.some(p => p.projectId === 'qCV9QHROSH')
+      });
+    } else {
+      console.warn(`⚠️ 未找到项目 qCV9QHROSH,可能数据还未加载`);
+    }
+    
     // 设计师筛选
     if (this.selectedDesigner !== 'all') {
       // 选择单个设计师:显示该设计师的所有项目