为了支持项目负载时间轴的阶段查看功能,需要在Project表中添加各个设计阶段的截止时间信息,包括:
根据schemas.md说明,这些信息应存储在Project.data字段(Object类型)中,可以自由扩展子级属性。
interface PhaseDeadlines {
modeling?: PhaseInfo; // 建模阶段
softDecor?: PhaseInfo; // 软装阶段
rendering?: PhaseInfo; // 渲染阶段
postProcessing?: PhaseInfo; // 后期阶段
}
interface PhaseInfo {
startDate?: Date; // 阶段开始时间
deadline: Date; // 阶段截止时间
estimatedDays?: number; // 预计工期(天数)
status?: 'not_started' | 'in_progress' | 'completed' | 'delayed'; // 阶段状态
completedAt?: Date; // 实际完成时间
assignee?: Pointer<Profile>; // 负责人
priority?: 'low' | 'medium' | 'high' | 'urgent'; // 优先级
notes?: string; // 备注信息
}
{
"phaseDeadlines": {
"modeling": {
"startDate": "2024-12-01T00:00:00.000Z",
"deadline": "2024-12-08T23:59:59.999Z",
"estimatedDays": 7,
"status": "completed",
"completedAt": "2024-12-07T18:30:00.000Z",
"assignee": {
"__type": "Pointer",
"className": "Profile",
"objectId": "prof001"
},
"priority": "high",
"notes": "客户要求加急,优先处理"
},
"softDecor": {
"startDate": "2024-12-09T00:00:00.000Z",
"deadline": "2024-12-13T23:59:59.999Z",
"estimatedDays": 4,
"status": "in_progress",
"assignee": {
"__type": "Pointer",
"className": "Profile",
"objectId": "prof002"
},
"priority": "medium"
},
"rendering": {
"startDate": "2024-12-14T00:00:00.000Z",
"deadline": "2024-12-20T23:59:59.999Z",
"estimatedDays": 6,
"status": "not_started",
"priority": "high",
"notes": "需要高质量渲染,预留充足时间"
},
"postProcessing": {
"startDate": "2024-12-21T00:00:00.000Z",
"deadline": "2024-12-24T23:59:59.999Z",
"estimatedDays": 3,
"status": "not_started",
"priority": "medium",
"notes": "后期处理与润色"
}
}
}
{
"phaseDeadlines": {
"modeling": {
"deadline": "2024-12-08T23:59:59.999Z",
"status": "in_progress"
},
"softDecor": {
"deadline": "2024-12-13T23:59:59.999Z",
"status": "not_started"
},
"rendering": {
"deadline": "2024-12-20T23:59:59.999Z",
"status": "not_started"
},
"postProcessing": {
"deadline": "2024-12-24T23:59:59.999Z",
"status": "not_started"
}
}
}
Parse.Cloud.define("createProjectWithPhases", async (request) => {
const { projectTitle, contactId, companyId, phaseConfig } = request.params;
const Project = Parse.Object.extend("Project");
const project = new Project();
project.set("title", projectTitle);
project.set("contact", { __type: "Pointer", className: "ContactInfo", objectId: contactId });
project.set("company", { __type: "Pointer", className: "Company", objectId: companyId });
project.set("status", "进行中");
project.set("currentStage", "建模");
// 设置阶段截止时间
const baseDate = new Date();
project.set("data", {
phaseDeadlines: {
modeling: {
startDate: new Date(baseDate),
deadline: new Date(baseDate.getTime() + 7 * 24 * 60 * 60 * 1000), // 7天后
estimatedDays: 7,
status: "in_progress",
priority: "high"
},
softDecor: {
startDate: new Date(baseDate.getTime() + 7 * 24 * 60 * 60 * 1000),
deadline: new Date(baseDate.getTime() + 11 * 24 * 60 * 60 * 1000), // 11天后
estimatedDays: 4,
status: "not_started",
priority: "medium"
},
rendering: {
startDate: new Date(baseDate.getTime() + 11 * 24 * 60 * 60 * 1000),
deadline: new Date(baseDate.getTime() + 17 * 24 * 60 * 60 * 1000), // 17天后
estimatedDays: 6,
status: "not_started",
priority: "high"
},
postProcessing: {
startDate: new Date(baseDate.getTime() + 17 * 24 * 60 * 60 * 1000),
deadline: new Date(baseDate.getTime() + 20 * 24 * 60 * 60 * 1000), // 20天后
estimatedDays: 3,
status: "not_started",
priority: "medium"
}
}
});
await project.save(null, { useMasterKey: true });
return project;
});
Parse.Cloud.define("updatePhaseStatus", async (request) => {
const { projectId, phaseName, status, completedAt } = request.params;
const projectQuery = new Parse.Query("Project");
const project = await projectQuery.get(projectId, { useMasterKey: true });
const data = project.get("data") || {};
const phaseDeadlines = data.phaseDeadlines || {};
if (!phaseDeadlines[phaseName]) {
throw new Error(`Phase ${phaseName} not found`);
}
phaseDeadlines[phaseName].status = status;
if (status === "completed") {
phaseDeadlines[phaseName].completedAt = completedAt || new Date();
}
data.phaseDeadlines = phaseDeadlines;
project.set("data", data);
await project.save(null, { useMasterKey: true });
return project;
});
Parse.Cloud.define("getProjectsByPhaseStatus", async (request) => {
const { companyId, phaseName, status } = request.params;
const projectQuery = new Parse.Query("Project");
projectQuery.equalTo("company", { __type: "Pointer", className: "Company", objectId: companyId });
projectQuery.exists(`data.phaseDeadlines.${phaseName}`);
const projects = await projectQuery.find({ useMasterKey: true });
// 前端过滤(Parse Query不支持深层嵌套查询)
return projects.filter(project => {
const data = project.get("data");
return data?.phaseDeadlines?.[phaseName]?.status === status;
});
});
// src/app/models/project-phase.model.ts
export interface PhaseInfo {
startDate?: Date;
deadline: Date;
estimatedDays?: number;
status?: 'not_started' | 'in_progress' | 'completed' | 'delayed';
completedAt?: Date;
assignee?: {
__type: 'Pointer';
className: 'Profile';
objectId: string;
};
priority?: 'low' | 'medium' | 'high' | 'urgent';
notes?: string;
}
export interface PhaseDeadlines {
modeling?: PhaseInfo;
softDecor?: PhaseInfo;
rendering?: PhaseInfo;
postProcessing?: PhaseInfo;
}
export interface ProjectData {
phaseDeadlines?: PhaseDeadlines;
// ... 其他data字段
}
// src/app/services/project.service.ts
getProjectPhaseDeadlines(projectId: string): Observable<PhaseDeadlines | null> {
const query = new Parse.Query('Project');
return from(query.get(projectId)).pipe(
map(project => {
const data = project.get('data') as ProjectData;
return data?.phaseDeadlines || null;
})
);
}
// src/app/pages/team-leader/project-timeline/project-timeline.component.ts
interface TimelineEvent {
date: Date;
label: string;
type: 'start' | 'milestone' | 'deadline';
phase: string;
status?: string;
}
generateTimelineEvents(project: any): TimelineEvent[] {
const events: TimelineEvent[] = [];
const phaseDeadlines = project.get('data')?.phaseDeadlines;
if (!phaseDeadlines) return events;
const phaseLabels = {
modeling: '建模',
softDecor: '软装',
rendering: '渲染',
postProcessing: '后期'
};
// 为每个阶段生成事件
Object.entries(phaseDeadlines).forEach(([phaseName, phaseInfo]: [string, any]) => {
// 开始事件
if (phaseInfo.startDate) {
events.push({
date: new Date(phaseInfo.startDate),
label: `${phaseLabels[phaseName as keyof typeof phaseLabels]}开始`,
type: 'start',
phase: phaseName,
status: phaseInfo.status
});
}
// 截止事件
if (phaseInfo.deadline) {
events.push({
date: new Date(phaseInfo.deadline),
label: `${phaseLabels[phaseName as keyof typeof phaseLabels]}截止`,
type: 'deadline',
phase: phaseName,
status: phaseInfo.status
});
}
// 完成事件
if (phaseInfo.completedAt) {
events.push({
date: new Date(phaseInfo.completedAt),
label: `${phaseLabels[phaseName as keyof typeof phaseLabels]}完成`,
type: 'milestone',
phase: phaseName,
status: 'completed'
});
}
});
// 按时间排序
return events.sort((a, b) => a.date.getTime() - b.date.getTime());
}
const phaseColors = {
modeling: {
bg: '#E3F2FD', // 浅蓝色
border: '#2196F3', // 蓝色
label: '建模'
},
softDecor: {
bg: '#F3E5F5', // 浅紫色
border: '#9C27B0', // 紫色
label: '软装'
},
rendering: {
bg: '#FFF3E0', // 浅橙色
border: '#FF9800', // 橙色
label: '渲染'
},
postProcessing: {
bg: '#E8F5E9', // 浅绿色
border: '#4CAF50', // 绿色
label: '后期'
}
};
const statusIcons = {
not_started: '⏸️',
in_progress: '▶️',
completed: '✅',
delayed: '⚠️'
};
Parse.Cloud.job("migrateProjectPhaseDeadlines", async (request) => {
const { params, message } = request;
const query = new Parse.Query("Project");
query.notEqualTo("isDeleted", true);
query.limit(1000);
let count = 0;
const projects = await query.find({ useMasterKey: true });
for (const project of projects) {
const data = project.get("data") || {};
// 如果已有phaseDeadlines,跳过
if (data.phaseDeadlines) continue;
// 根据项目deadline推算各阶段截止时间
const deadline = project.get("deadline");
if (!deadline) continue;
const deadlineTime = deadline.getTime();
const modelingDeadline = new Date(deadlineTime - 13 * 24 * 60 * 60 * 1000); // 提前13天
const softDecorDeadline = new Date(deadlineTime - 9 * 24 * 60 * 60 * 1000); // 提前9天
const renderingDeadline = new Date(deadlineTime - 3 * 24 * 60 * 60 * 1000); // 提前3天
const postProcessingDeadline = deadline;
data.phaseDeadlines = {
modeling: {
deadline: modelingDeadline,
estimatedDays: 7,
status: "not_started"
},
softDecor: {
deadline: softDecorDeadline,
estimatedDays: 4,
status: "not_started"
},
rendering: {
deadline: renderingDeadline,
estimatedDays: 6,
status: "not_started"
},
postProcessing: {
deadline: postProcessingDeadline,
estimatedDays: 3,
status: "not_started"
}
};
project.set("data", data);
await project.save(null, { useMasterKey: true });
count++;
message(`Migrated ${count} projects`);
}
message(`Migration completed: ${count} projects updated`);
});
建议在Company.data中添加默认工期配置:
{
"phaseDefaultDurations": {
"modeling": 7, // 建模默认7天
"softDecor": 4, // 软装默认4天
"rendering": 6, // 渲染默认6天
"postProcessing": 3 // 后期默认3天
}
}
async createProjectWithDefaultPhases(
projectData: any,
companyId: string
): Promise<Parse.Object> {
// 获取公司配置
const companyQuery = new Parse.Query('Company');
const company = await companyQuery.get(companyId);
const companyData = company.get('data') || {};
const durations = companyData.phaseDefaultDurations || {
modeling: 7,
softDecor: 4,
rendering: 6,
postProcessing: 3
};
// 计算各阶段截止时间
const startDate = new Date();
const phaseDeadlines: any = {};
let currentDate = new Date(startDate);
['modeling', 'softDecor', 'rendering', 'postProcessing'].forEach(phase => {
const days = durations[phase];
const deadline = new Date(currentDate.getTime() + days * 24 * 60 * 60 * 1000);
phaseDeadlines[phase] = {
startDate: new Date(currentDate),
deadline: deadline,
estimatedDays: days,
status: phase === 'modeling' ? 'in_progress' : 'not_started',
priority: 'medium'
};
currentDate = new Date(deadline.getTime() + 1); // 下一阶段从前一阶段结束后开始
});
// 创建项目
const Project = Parse.Object.extend('Project');
const project = new Project();
project.set('data', { phaseDeadlines, ...projectData.data });
// ... 设置其他字段
await project.save(null, { useMasterKey: true });
return project;
}
deadline字段为有效的Date对象phaseDeadlines字段project.get('data')?.phaseDeadlines在Project表的data字段说明中添加phaseDeadlines结构说明
为现有项目添加默认的阶段截止时间
添加PhaseInfo和PhaseDeadlines接口定义
在创建项目时自动生成阶段截止时间
读取并展示阶段截止时间信息
允许手动调整各阶段的截止时间和状态
通过在Project.data.phaseDeadlines中存储各阶段截止时间信息:
建议按照本方案实施,可以有效支持项目负载图的阶段查看功能。