|
|
@@ -415,6 +415,9 @@ export class Dashboard implements OnInit, OnDestroy {
|
|
|
finalCreatedAt = project.updatedAt; // Parse 内置属性
|
|
|
}
|
|
|
|
|
|
+ // ✅ 应用方案:获取项目的 data 字段(包含 phaseDeadlines, deliveryStageStatus 等)
|
|
|
+ const projectDataField = project.get('data') || {};
|
|
|
+
|
|
|
const projectData = {
|
|
|
id: project.id,
|
|
|
name: project.get('title') || '未命名项目',
|
|
|
@@ -423,7 +426,12 @@ export class Dashboard implements OnInit, OnDestroy {
|
|
|
deadline: deadlineValue || deliveryDateValue || expectedDeliveryDateValue,
|
|
|
demoday: demodayValue, // 🆕 小图对图日期
|
|
|
createdAt: finalCreatedAt,
|
|
|
- designerName: profileName
|
|
|
+ updatedAt: updatedAtValue || project.updatedAt, // ✅ 添加 updatedAt
|
|
|
+ designerName: profileName,
|
|
|
+ designerId: profileId, // ✅ 添加 designerId
|
|
|
+ data: projectDataField, // ✅ 添加 data 字段
|
|
|
+ contact: project.get('contact'), // ✅ 添加客户信息
|
|
|
+ space: projectDataField.quotation?.spaces?.[0]?.name || '' // ✅ 添加空间信息
|
|
|
};
|
|
|
|
|
|
// 添加到映射 (by ID)
|
|
|
@@ -499,6 +507,9 @@ export class Dashboard implements OnInit, OnDestroy {
|
|
|
finalCreatedAt = project.updatedAt;
|
|
|
}
|
|
|
|
|
|
+ // ✅ 应用方案:获取项目的 data 字段(包含 phaseDeadlines, deliveryStageStatus 等)
|
|
|
+ const projectDataField = project.get('data') || {};
|
|
|
+
|
|
|
const projectData = {
|
|
|
id: project.id,
|
|
|
name: project.get('title') || '未命名项目',
|
|
|
@@ -507,7 +518,12 @@ export class Dashboard implements OnInit, OnDestroy {
|
|
|
deadline: deadlineValue || deliveryDateValue || expectedDeliveryDateValue,
|
|
|
demoday: demodayValue, // 🆕 小图对图日期
|
|
|
createdAt: finalCreatedAt,
|
|
|
- designerName: assigneeName
|
|
|
+ updatedAt: updatedAtValue || project.updatedAt, // ✅ 添加 updatedAt
|
|
|
+ designerName: assigneeName,
|
|
|
+ designerId: assignee.id, // ✅ 添加 designerId
|
|
|
+ data: projectDataField, // ✅ 添加 data 字段
|
|
|
+ contact: project.get('contact'), // ✅ 添加客户信息
|
|
|
+ space: projectDataField.quotation?.spaces?.[0]?.name || '' // ✅ 添加空间信息
|
|
|
};
|
|
|
|
|
|
// 添加到映射
|
|
|
@@ -593,56 +609,55 @@ export class Dashboard implements OnInit, OnDestroy {
|
|
|
|
|
|
this.projectTimelineData = allDesignerProjects.map((project, index) => {
|
|
|
const now = new Date();
|
|
|
- const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
|
|
|
-
|
|
|
- // 🎨 测试专用:精心设计项目分布,展示不同负载状态
|
|
|
- // 目标效果:
|
|
|
- // 第1天(今天)=1项目(忙碌🔵), 第2天(明天)=0项目(空闲🟢), 第3天=3项目(超负荷🔴),
|
|
|
- // 第4天=2项目(忙碌🔵), 第5天=1项目(忙碌🔵), 第6天=4项目(超负荷🔴), 第7天=2项目(忙碌🔵)
|
|
|
-
|
|
|
- // 使用项目索引映射到具体天数,跳过第2天以实现0项目效果
|
|
|
- const dayMapping = [
|
|
|
- 1, // 项目0 → 第1天
|
|
|
- 3, 3, 3, // 项目1,2,3 → 第3天(3个项目,超负荷)
|
|
|
- 4, 4, // 项目4,5 → 第4天(2个项目)
|
|
|
- 5, // 项目6 → 第5天(1个项目)
|
|
|
- 6, 6, 6, 6, // 项目7,8,9,10 → 第6天(4个项目,超负荷)
|
|
|
- 7, 7 // 项目11,12 → 第7天(2个项目)
|
|
|
- ];
|
|
|
|
|
|
- let dayOffset: number;
|
|
|
+ // ✅ 应用方案:使用真实字段数据
|
|
|
+ const projectData = project.data || {};
|
|
|
|
|
|
- if (index < dayMapping.length) {
|
|
|
- dayOffset = dayMapping[index];
|
|
|
+ // 1. 获取真实的项目开始时间
|
|
|
+ // 优先使用 phaseDeadlines.modeling.startDate,其次使用 requirementsConfirmedAt,最后使用 createdAt
|
|
|
+ let realStartDate: Date;
|
|
|
+ if (projectData.phaseDeadlines?.modeling?.startDate) {
|
|
|
+ realStartDate = projectData.phaseDeadlines.modeling.startDate instanceof Date
|
|
|
+ ? projectData.phaseDeadlines.modeling.startDate
|
|
|
+ : new Date(projectData.phaseDeadlines.modeling.startDate);
|
|
|
+ } else if (projectData.requirementsConfirmedAt) {
|
|
|
+ realStartDate = new Date(projectData.requirementsConfirmedAt);
|
|
|
+ } else if (project.createdAt) {
|
|
|
+ realStartDate = project.createdAt instanceof Date ? project.createdAt : new Date(project.createdAt);
|
|
|
} else {
|
|
|
- // 超出13个项目后,分配到后续天数
|
|
|
- dayOffset = 7 + ((index - dayMapping.length) % 7) + 1;
|
|
|
+ // 降级:如果没有开始时间,使用当前时间
|
|
|
+ realStartDate = new Date();
|
|
|
}
|
|
|
|
|
|
- const adjustedEndDate = new Date(today.getTime() + dayOffset * 24 * 60 * 60 * 1000);
|
|
|
-
|
|
|
- // 项目开始时间:交付前3-7天
|
|
|
- const projectDuration = 3 + (index % 5); // 3-7天的项目周期
|
|
|
- const adjustedStartDate = new Date(adjustedEndDate.getTime() - projectDuration * 24 * 60 * 60 * 1000);
|
|
|
+ // 2. 获取真实的交付日期
|
|
|
+ let realEndDate: Date;
|
|
|
+ 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);
|
|
|
+ }
|
|
|
|
|
|
- // 🆕 小图对图时间:设置在软装和渲染之间,便于展示
|
|
|
- let adjustedReviewDate: Date;
|
|
|
- if (project.demoday && project.demoday instanceof Date) {
|
|
|
- // 使用真实的小图对图日期
|
|
|
- adjustedReviewDate = project.demoday;
|
|
|
+ // 3. 获取真实的对图时间
|
|
|
+ // 优先使用 demoday,其次使用 phaseDeadlines.softDecor.deadline,最后计算
|
|
|
+ let realReviewDate: Date;
|
|
|
+ if (project.demoday) {
|
|
|
+ realReviewDate = project.demoday instanceof Date ? project.demoday : new Date(project.demoday);
|
|
|
+ } else if (projectData.phaseDeadlines?.softDecor?.deadline) {
|
|
|
+ const softDecorDeadline = projectData.phaseDeadlines.softDecor.deadline;
|
|
|
+ realReviewDate = softDecorDeadline instanceof Date ? softDecorDeadline : new Date(softDecorDeadline);
|
|
|
} else {
|
|
|
- // 🔥 修改为便于展示:设置在项目时间轴的中间位置(软装完成后)
|
|
|
- // 计算项目周期的 60% 位置(软装后、渲染前)
|
|
|
- const projectMidPoint = adjustedStartDate.getTime() + (projectDuration * 0.6 * 24 * 60 * 60 * 1000);
|
|
|
- adjustedReviewDate = new Date(projectMidPoint);
|
|
|
- // 设置具体时间为下午2点
|
|
|
- adjustedReviewDate.setHours(14, 0, 0, 0);
|
|
|
+ // 计算:设置在软装和渲染之间(项目周期的 60% 位置)
|
|
|
+ const projectDuration = realEndDate.getTime() - realStartDate.getTime();
|
|
|
+ const projectMidPoint = realStartDate.getTime() + (projectDuration * 0.6);
|
|
|
+ realReviewDate = new Date(projectMidPoint);
|
|
|
+ realReviewDate.setHours(14, 0, 0, 0);
|
|
|
}
|
|
|
|
|
|
- // 计算距离交付还有几天
|
|
|
- const daysUntilDeadline = Math.ceil((adjustedEndDate.getTime() - now.getTime()) / (1000 * 60 * 60 * 24));
|
|
|
+ // 4. 计算距离交付还有几天(使用真实日期)
|
|
|
+ const daysUntilDeadline = Math.ceil((realEndDate.getTime() - now.getTime()) / (1000 * 60 * 60 * 24));
|
|
|
|
|
|
- // 计算项目状态
|
|
|
+ // 5. 计算项目状态
|
|
|
let status: 'normal' | 'warning' | 'urgent' | 'overdue' = 'normal';
|
|
|
if (daysUntilDeadline < 0) {
|
|
|
status = 'overdue';
|
|
|
@@ -652,7 +667,7 @@ export class Dashboard implements OnInit, OnDestroy {
|
|
|
status = 'warning';
|
|
|
}
|
|
|
|
|
|
- // 映射阶段
|
|
|
+ // 6. 映射阶段
|
|
|
const stageMap: Record<string, 'plan' | 'model' | 'decoration' | 'render' | 'delivery'> = {
|
|
|
'方案设计': 'plan',
|
|
|
'方案规划': 'plan',
|
|
|
@@ -669,19 +684,75 @@ export class Dashboard implements OnInit, OnDestroy {
|
|
|
const currentStage = stageMap[project.currentStage || '建模阶段'] || 'model';
|
|
|
const stageName = project.currentStage || '建模阶段';
|
|
|
|
|
|
- // 计算阶段进度
|
|
|
- const totalDuration = adjustedEndDate.getTime() - adjustedStartDate.getTime();
|
|
|
- const elapsed = now.getTime() - adjustedStartDate.getTime();
|
|
|
- const stageProgress = totalDuration > 0 ? Math.min(100, Math.max(0, (elapsed / totalDuration) * 100)) : 50;
|
|
|
+ // 7. 计算真实的阶段进度(基于 phaseDeadlines)
|
|
|
+ let stageProgress = 50; // 默认值
|
|
|
+ if (projectData.phaseDeadlines) {
|
|
|
+ const phaseDeadlines = projectData.phaseDeadlines;
|
|
|
+
|
|
|
+ // 根据当前阶段计算进度
|
|
|
+ if (currentStage === 'model' && phaseDeadlines.modeling) {
|
|
|
+ const start = phaseDeadlines.modeling.startDate instanceof Date
|
|
|
+ ? phaseDeadlines.modeling.startDate
|
|
|
+ : new Date(phaseDeadlines.modeling.startDate);
|
|
|
+ const end = phaseDeadlines.modeling.deadline instanceof Date
|
|
|
+ ? phaseDeadlines.modeling.deadline
|
|
|
+ : new Date(phaseDeadlines.modeling.deadline);
|
|
|
+ const total = end.getTime() - start.getTime();
|
|
|
+ const elapsed = now.getTime() - start.getTime();
|
|
|
+ stageProgress = total > 0 ? Math.min(100, Math.max(0, (elapsed / total) * 100)) : 50;
|
|
|
+ } else if (currentStage === 'decoration' && phaseDeadlines.softDecor) {
|
|
|
+ const start = phaseDeadlines.softDecor.startDate instanceof Date
|
|
|
+ ? phaseDeadlines.softDecor.startDate
|
|
|
+ : new Date(phaseDeadlines.softDecor.startDate);
|
|
|
+ const end = phaseDeadlines.softDecor.deadline instanceof Date
|
|
|
+ ? phaseDeadlines.softDecor.deadline
|
|
|
+ : new Date(phaseDeadlines.softDecor.deadline);
|
|
|
+ const total = end.getTime() - start.getTime();
|
|
|
+ const elapsed = now.getTime() - start.getTime();
|
|
|
+ stageProgress = total > 0 ? Math.min(100, Math.max(0, (elapsed / total) * 100)) : 50;
|
|
|
+ } else if (currentStage === 'render' && phaseDeadlines.rendering) {
|
|
|
+ const start = phaseDeadlines.rendering.startDate instanceof Date
|
|
|
+ ? phaseDeadlines.rendering.startDate
|
|
|
+ : new Date(phaseDeadlines.rendering.startDate);
|
|
|
+ const end = phaseDeadlines.rendering.deadline instanceof Date
|
|
|
+ ? phaseDeadlines.rendering.deadline
|
|
|
+ : new Date(phaseDeadlines.rendering.deadline);
|
|
|
+ const total = end.getTime() - start.getTime();
|
|
|
+ const elapsed = now.getTime() - start.getTime();
|
|
|
+ stageProgress = total > 0 ? Math.min(100, Math.max(0, (elapsed / total) * 100)) : 50;
|
|
|
+ } else if (currentStage === 'delivery' && phaseDeadlines.postProcessing) {
|
|
|
+ const start = phaseDeadlines.postProcessing.startDate instanceof Date
|
|
|
+ ? phaseDeadlines.postProcessing.startDate
|
|
|
+ : new Date(phaseDeadlines.postProcessing.startDate);
|
|
|
+ const end = phaseDeadlines.postProcessing.deadline instanceof Date
|
|
|
+ ? phaseDeadlines.postProcessing.deadline
|
|
|
+ : new Date(phaseDeadlines.postProcessing.deadline);
|
|
|
+ const total = end.getTime() - start.getTime();
|
|
|
+ const elapsed = now.getTime() - start.getTime();
|
|
|
+ stageProgress = total > 0 ? Math.min(100, Math.max(0, (elapsed / total) * 100)) : 50;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // 降级:使用整体项目进度
|
|
|
+ const totalDuration = realEndDate.getTime() - realStartDate.getTime();
|
|
|
+ const elapsed = now.getTime() - realStartDate.getTime();
|
|
|
+ stageProgress = totalDuration > 0 ? Math.min(100, Math.max(0, (elapsed / totalDuration) * 100)) : 50;
|
|
|
+ }
|
|
|
|
|
|
- // 检查是否停滞
|
|
|
- const isStalled = false; // 调整后的项目都是进行中
|
|
|
- const stalledDays = 0;
|
|
|
+ // 8. 检查是否停滞(基于 updatedAt)
|
|
|
+ let isStalled = false;
|
|
|
+ let stalledDays = 0;
|
|
|
+ if (project.updatedAt) {
|
|
|
+ const updatedAt = project.updatedAt instanceof Date ? project.updatedAt : new Date(project.updatedAt);
|
|
|
+ const daysSinceUpdate = Math.floor((now.getTime() - updatedAt.getTime()) / (1000 * 60 * 60 * 24));
|
|
|
+ // 如果超过7天未更新,认为停滞
|
|
|
+ isStalled = daysSinceUpdate > 7;
|
|
|
+ stalledDays = isStalled ? daysSinceUpdate : 0;
|
|
|
+ }
|
|
|
|
|
|
- // 催办次数
|
|
|
+ // 9. 催办次数(基于状态和历史记录)
|
|
|
const urgentCount = status === 'overdue' ? 2 : status === 'urgent' ? 1 : 0;
|
|
|
|
|
|
- // 优先级
|
|
|
+ // 10. 优先级
|
|
|
let priority: 'low' | 'medium' | 'high' | 'critical' = 'medium';
|
|
|
if (status === 'overdue') {
|
|
|
priority = 'critical';
|
|
|
@@ -693,57 +764,72 @@ export class Dashboard implements OnInit, OnDestroy {
|
|
|
priority = 'low';
|
|
|
}
|
|
|
|
|
|
- // 🆕 生成阶段截止时间数据(从交付日期往前推,每个阶段1天)
|
|
|
- let phaseDeadlines = project.data?.phaseDeadlines;
|
|
|
+ // 11. 获取或生成阶段截止时间数据
|
|
|
+ let phaseDeadlines = projectData.phaseDeadlines;
|
|
|
|
|
|
- // 如果项目没有阶段数据,动态生成(用于演示效果)
|
|
|
- if (!phaseDeadlines) {
|
|
|
- // ✅ 关键修复:从交付日期往前推算各阶段截止时间
|
|
|
- const deliveryTime = adjustedEndDate.getTime();
|
|
|
+ // 如果项目没有阶段数据,从交付日期往前推算
|
|
|
+ if (!phaseDeadlines && realEndDate) {
|
|
|
+ const deliveryTime = realEndDate.getTime();
|
|
|
+ const startTime = realStartDate.getTime();
|
|
|
+ const totalDays = Math.ceil((deliveryTime - startTime) / (24 * 60 * 60 * 1000));
|
|
|
+
|
|
|
+ // 按比例分配:建模30%,软装25%,渲染30%,后期15%
|
|
|
+ const modelingDays = Math.ceil(totalDays * 0.3);
|
|
|
+ const softDecorDays = Math.ceil(totalDays * 0.25);
|
|
|
+ const renderingDays = Math.ceil(totalDays * 0.3);
|
|
|
+ const postProcessDays = totalDays - modelingDays - softDecorDays - renderingDays;
|
|
|
+
|
|
|
+ let currentDate = new Date(startTime);
|
|
|
|
|
|
- // 后期截止 = 交付日期 - 1天(确保后期事件在交付前显示)
|
|
|
- const postProcessingDeadline = new Date(deliveryTime - 1 * 24 * 60 * 60 * 1000);
|
|
|
- // 渲染截止 = 交付日期 - 2天
|
|
|
- const renderingDeadline = new Date(deliveryTime - 2 * 24 * 60 * 60 * 1000);
|
|
|
- // 软装截止 = 交付日期 - 3天
|
|
|
- const softDecorDeadline = new Date(deliveryTime - 3 * 24 * 60 * 60 * 1000);
|
|
|
- // 建模截止 = 交付日期 - 4天
|
|
|
- const modelingDeadline = new Date(deliveryTime - 4 * 24 * 60 * 60 * 1000);
|
|
|
+ const modelingDeadline = new Date(currentDate);
|
|
|
+ modelingDeadline.setDate(modelingDeadline.getDate() + modelingDays);
|
|
|
|
|
|
- // 🔥 小图对图时间 = 交付日期 - 2.5天(软装和渲染之间)
|
|
|
- const reviewTime = deliveryTime - 2.5 * 24 * 60 * 60 * 1000;
|
|
|
- adjustedReviewDate = new Date(reviewTime); // ⚠️ 不要使用 const,直接赋值给外层变量
|
|
|
- adjustedReviewDate.setHours(14, 0, 0, 0); // 设置时间为下午2点
|
|
|
+ currentDate = new Date(modelingDeadline);
|
|
|
+ const softDecorDeadline = new Date(currentDate);
|
|
|
+ softDecorDeadline.setDate(softDecorDeadline.getDate() + softDecorDays);
|
|
|
+
|
|
|
+ currentDate = new Date(softDecorDeadline);
|
|
|
+ const renderingDeadline = new Date(currentDate);
|
|
|
+ renderingDeadline.setDate(renderingDeadline.getDate() + renderingDays);
|
|
|
+
|
|
|
+ currentDate = new Date(renderingDeadline);
|
|
|
+ const postProcessingDeadline = new Date(currentDate);
|
|
|
+ postProcessingDeadline.setDate(postProcessingDeadline.getDate() + postProcessDays);
|
|
|
+
|
|
|
+ // 更新对图时间(软装和渲染之间)
|
|
|
+ const reviewTime = softDecorDeadline.getTime() + (renderingDeadline.getTime() - softDecorDeadline.getTime()) * 0.5;
|
|
|
+ realReviewDate = new Date(reviewTime);
|
|
|
+ realReviewDate.setHours(14, 0, 0, 0);
|
|
|
|
|
|
phaseDeadlines = {
|
|
|
modeling: {
|
|
|
- startDate: adjustedStartDate,
|
|
|
+ startDate: realStartDate,
|
|
|
deadline: modelingDeadline,
|
|
|
- estimatedDays: 1,
|
|
|
- status: now.getTime() >= modelingDeadline.getTime() && now.getTime() < softDecorDeadline.getTime() ? 'in_progress' :
|
|
|
- now.getTime() >= softDecorDeadline.getTime() ? 'completed' : 'not_started',
|
|
|
+ estimatedDays: modelingDays,
|
|
|
+ status: now.getTime() >= modelingDeadline.getTime() ? 'completed' :
|
|
|
+ now.getTime() >= realStartDate.getTime() ? 'in_progress' : 'not_started',
|
|
|
priority: 'high'
|
|
|
},
|
|
|
softDecor: {
|
|
|
startDate: modelingDeadline,
|
|
|
deadline: softDecorDeadline,
|
|
|
- estimatedDays: 1,
|
|
|
- status: now.getTime() >= softDecorDeadline.getTime() && now.getTime() < renderingDeadline.getTime() ? 'in_progress' :
|
|
|
- now.getTime() >= renderingDeadline.getTime() ? 'completed' : 'not_started',
|
|
|
+ estimatedDays: softDecorDays,
|
|
|
+ status: now.getTime() >= softDecorDeadline.getTime() ? 'completed' :
|
|
|
+ now.getTime() >= modelingDeadline.getTime() ? 'in_progress' : 'not_started',
|
|
|
priority: 'medium'
|
|
|
},
|
|
|
rendering: {
|
|
|
startDate: softDecorDeadline,
|
|
|
deadline: renderingDeadline,
|
|
|
- estimatedDays: 1,
|
|
|
- status: now.getTime() >= renderingDeadline.getTime() && now.getTime() < postProcessingDeadline.getTime() ? 'in_progress' :
|
|
|
- now.getTime() >= postProcessingDeadline.getTime() ? 'completed' : 'not_started',
|
|
|
+ estimatedDays: renderingDays,
|
|
|
+ status: now.getTime() >= renderingDeadline.getTime() ? 'completed' :
|
|
|
+ now.getTime() >= softDecorDeadline.getTime() ? 'in_progress' : 'not_started',
|
|
|
priority: 'high'
|
|
|
},
|
|
|
postProcessing: {
|
|
|
startDate: renderingDeadline,
|
|
|
deadline: postProcessingDeadline,
|
|
|
- estimatedDays: 1,
|
|
|
+ estimatedDays: postProcessDays,
|
|
|
status: now.getTime() >= postProcessingDeadline.getTime() ? 'completed' :
|
|
|
now.getTime() >= renderingDeadline.getTime() ? 'in_progress' : 'not_started',
|
|
|
priority: 'medium'
|
|
|
@@ -751,26 +837,31 @@ export class Dashboard implements OnInit, OnDestroy {
|
|
|
};
|
|
|
}
|
|
|
|
|
|
+ // 12. 获取空间和客户信息
|
|
|
+ const spaceName = project.space || projectData.quotation?.spaces?.[0]?.name || '';
|
|
|
+ const customerName = project.customer || project.contact?.name || '';
|
|
|
+
|
|
|
return {
|
|
|
projectId: project.id || `proj-${Math.random().toString(36).slice(2, 9)}`,
|
|
|
projectName: project.name || '未命名项目',
|
|
|
- designerId: project.designerName || '未分配',
|
|
|
+ designerId: project.designerId || project.designerName || '未分配',
|
|
|
designerName: project.designerName || '未分配',
|
|
|
- startDate: adjustedStartDate,
|
|
|
- endDate: adjustedEndDate,
|
|
|
- deliveryDate: adjustedEndDate,
|
|
|
- reviewDate: adjustedReviewDate,
|
|
|
+ startDate: realStartDate, // ✅ 使用真实开始时间
|
|
|
+ endDate: realEndDate, // ✅ 使用真实结束时间
|
|
|
+ deliveryDate: realEndDate, // ✅ 使用真实交付日期
|
|
|
+ reviewDate: realReviewDate, // ✅ 使用真实对图时间
|
|
|
currentStage,
|
|
|
stageName,
|
|
|
- stageProgress: Math.round(stageProgress),
|
|
|
- status,
|
|
|
- isStalled,
|
|
|
- stalledDays,
|
|
|
+ stageProgress: Math.round(stageProgress), // ✅ 使用计算的真实进度
|
|
|
+ status, // ✅ 基于真实日期计算的状态
|
|
|
+ isStalled, // ✅ 基于 updatedAt 计算的停滞状态
|
|
|
+ stalledDays, // ✅ 真实的停滞天数
|
|
|
urgentCount,
|
|
|
priority,
|
|
|
- spaceName: project.space || '',
|
|
|
- customerName: project.customer || '',
|
|
|
- phaseDeadlines: phaseDeadlines // 🆕 阶段截止时间
|
|
|
+ spaceName, // ✅ 从项目数据获取
|
|
|
+ customerName, // ✅ 从项目数据获取
|
|
|
+ phaseDeadlines: phaseDeadlines, // ✅ 使用真实或计算的阶段截止时间
|
|
|
+ data: projectData // ✅ 保留原始数据,供后续使用
|
|
|
};
|
|
|
});
|
|
|
|