文件位置: src/app/models/project-phase.model.ts
包含内容:
PhaseInfo - 单个阶段信息接口PhaseDeadlines - 阶段截止时间集合接口ProjectData - Project.data字段类型定义PHASE_INFO - 阶段常量配置(建模/软装/渲染/后期)PHASE_STATUS_INFO - 阶段状态信息PHASE_PRIORITY_INFO - 优先级信息generateDefaultPhaseDeadlines() - 生成默认阶段截止时间isPhaseDelayed() - 检查阶段是否延期getPhaseDaysRemaining() - 获取阶段剩余天数getPhaseProgress() - 获取阶段进度百分比文件位置:
src/app/pages/team-leader/project-timeline/project-timeline.tssrc/app/pages/team-leader/project-timeline/project-timeline.html更新内容:
ProjectTimeline 接口添加 phaseDeadlines 字段TimelineEvent 接口支持多种事件类型getProjectEvents() 方法统一获取所有事件(含阶段截止)getPhaseLabel() - 获取阶段标签getPhaseIcon() - 获取阶段图标getPhaseColor() - 获取阶段颜色文件位置: src/app/pages/team-leader/dashboard/dashboard.ts
更新内容:
convertToProjectTimeline() 方法中读取 project.data.phaseDeadlines文件位置: cloud/jobs/migrate-project-phase-deadlines.js
功能:
migrateProjectPhaseDeadlines - 批量为现有项目添加阶段截止时间
dryRun: true)batchSize: 100)testProjectPhaseDeadlines - 测试单个项目的阶段时间生成文件位置: cloud/utils/project-phase-utils.js
功能:
generatePhaseDeadlines() - 生成阶段截止时间getCompanyPhaseDurations() - 获取公司级默认工期配置updatePhaseStatus() - 更新阶段状态getCurrentPhase() - 获取当前阶段isPhaseDelayed() - 检查是否延期getPhaseDaysRemaining() - 获取剩余天数Cloud Function:
generateProjectPhaseDeadlines - 生成阶段截止时间updateProjectPhaseStatus - 更新阶段状态文件位置: docs/schema/project-phase-deadlines-design.md
包含完整的:
import { PhaseDeadlines, PHASE_INFO, generateDefaultPhaseDeadlines } from '../models/project-phase.model';
// 生成默认阶段截止时间
const phaseDeadlines = generateDefaultPhaseDeadlines(new Date());
// 访问阶段信息
const modelingPhase = phaseDeadlines.modeling;
console.log('建模截止时间:', modelingPhase?.deadline);
// 使用常量获取阶段信息
const phaseConfig = PHASE_INFO.modeling;
console.log('建模阶段:', phaseConfig.label, phaseConfig.icon, phaseConfig.color);
时间轴组件已自动支持阶段截止时间展示,只需确保传入的项目数据包含 phaseDeadlines 字段:
const projectData: ProjectTimeline = {
projectId: 'xxx',
projectName: '李总现代简约全案',
// ... 其他字段
phaseDeadlines: {
modeling: {
deadline: new Date('2024-12-08'),
status: 'in_progress',
estimatedDays: 7
},
// ... 其他阶段
}
};
// 干跑模式(只计算不保存)
Parse.Cloud.startJob('migrateProjectPhaseDeadlines', {
dryRun: true,
batchSize: 50
});
// 正式迁移
Parse.Cloud.startJob('migrateProjectPhaseDeadlines', {
dryRun: false,
batchSize: 100
});
// 在afterSave钩子中自动生成
Parse.Cloud.afterSave("Project", async (request) => {
const project = request.object;
// 检查是否是新项目且有deadline
if (project.existed() || !project.get("deadline")) {
return;
}
const data = project.get("data") || {};
// 如果已经有phaseDeadlines,跳过
if (data.phaseDeadlines) {
return;
}
// 生成阶段截止时间
const { generatePhaseDeadlines } = require('./utils/project-phase-utils');
const phaseDeadlines = generatePhaseDeadlines(
project.get("createdAt"),
project.get("deadline")
);
data.phaseDeadlines = phaseDeadlines;
project.set("data", data);
await project.save(null, { useMasterKey: true });
});
// 方式1:使用Cloud Function
await Parse.Cloud.run('updateProjectPhaseStatus', {
projectId: 'xxx',
phaseName: 'modeling',
status: 'completed',
additionalData: {
completedAt: new Date(),
notes: '建模阶段已完成'
}
});
// 方式2:直接使用工具函数
const { updatePhaseStatus } = require('./utils/project-phase-utils');
await updatePhaseStatus('projectId', 'modeling', 'completed', {
completedAt: new Date()
});
// 在Company.data中添加配置
const company = await new Parse.Query("Company").get(companyId);
const data = company.get("data") || {};
data.phaseDefaultDurations = {
modeling: 8, // 建模8天
softDecor: 5, // 软装5天
rendering: 7, // 渲染7天
postProcessing: 4 // 后期4天
};
company.set("data", data);
await company.save(null, { useMasterKey: true });
{
"phaseDeadlines": {
"modeling": {
"startDate": "2024-12-01T00:00:00.000Z",
"deadline": "2024-12-08T23:59:59.999Z",
"estimatedDays": 7,
"status": "in_progress",
"completedAt": "2024-12-07T18:30:00.000Z",
"assignee": {
"__type": "Pointer",
"className": "Profile",
"objectId": "prof001"
},
"priority": "high",
"notes": "客户要求加急"
},
"softDecor": {
"deadline": "2024-12-13T23:59:59.999Z",
"estimatedDays": 4,
"status": "not_started",
"priority": "medium"
},
"rendering": {
"deadline": "2024-12-20T23:59:59.999Z",
"estimatedDays": 6,
"status": "not_started",
"priority": "high"
},
"postProcessing": {
"deadline": "2024-12-24T23:59:59.999Z",
"estimatedDays": 3,
"status": "not_started",
"priority": "medium"
}
}
}
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| startDate | Date | 否 | 阶段开始时间 |
| deadline | Date | 是 | 阶段截止时间 |
| estimatedDays | Number | 否 | 预计工期(天数) |
| status | String | 否 | 阶段状态 (not_started/in_progress/completed/delayed) |
| completedAt | Date | 否 | 实际完成时间 |
| assignee | Pointer | 否 | 负责人 |
| priority | String | 否 | 优先级 (low/medium/high/urgent) |
| notes | String | 否 | 备注信息 |
describe('PhaseDeadlines', () => {
it('should generate default phase deadlines', () => {
const startDate = new Date('2024-12-01');
const phaseDeadlines = generateDefaultPhaseDeadlines(startDate);
expect(phaseDeadlines.modeling).toBeDefined();
expect(phaseDeadlines.softDecor).toBeDefined();
expect(phaseDeadlines.rendering).toBeDefined();
expect(phaseDeadlines.postProcessing).toBeDefined();
});
it('should check if phase is delayed', () => {
const pastPhase: PhaseInfo = {
deadline: new Date('2024-01-01'),
status: 'in_progress'
};
expect(isPhaseDelayed(pastPhase)).toBe(true);
const futurePhase: PhaseInfo = {
deadline: new Date('2025-12-31'),
status: 'in_progress'
};
expect(isPhaseDelayed(futurePhase)).toBe(false);
});
});
原因:项目数据中没有 phaseDeadlines 字段
解决方案:
phaseDeadlines 数据原因:项目没有 deadline 字段
解决方案:
确保所有项目都设置了 deadline 字段
原因:项目数量过多
解决方案:
如有问题,请参考:
docs/schema/project-phase-deadlines-design.mdrules/schemas.md最后更新: 2024年11月6日
版本: 1.0.0