فهرست منبع

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 10 ماه پیش
والد
کامیت
1220a74b70
2فایلهای تغییر یافته به همراه153 افزوده شده و 5 حذف شده
  1. 63 3
      src/app/pages/team-leader/dashboard/dashboard.ts
  2. 90 2
      src/app/pages/team-leader/project-timeline/project-timeline.ts

+ 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') {
       // 选择单个设计师:显示该设计师的所有项目