
本文详解如何基于原始项目数组(含 workitems 子数组),通过链式 map 构建具备三级嵌套结构的规范化任务树,重点解决 taskid 层级编号、日期安全解析及子任务递归映射问题。
本文详解如何基于原始项目数组(含 workitems 子数组),通过链式 map 构建具备三级嵌套结构的规范化任务树,重点解决 taskid 层级编号、日期安全解析及子任务递归映射问题。
在构建项目管理类应用(如甘特图、任务看板)时,常需将扁平化的后端数据(如 projects 数组,其每个元素含 workItems 字段)转换为符合前端组件要求的深度嵌套对象树。核心挑战在于:既要保持外层项目结构完整,又需为每层子任务生成语义化 ID(如 1.2.3)、正确继承时间属性,并支持任意深度嵌套(如 workItem 下还存在 subtasks 数组)。
以下是一个健壮、可扩展的实现方案:
// ✅ 安全解析日期的辅助函数(避免 new Date("invalid") 返回 Invalid Date)
const safeDate = (dateStr) => {
const d = new Date(dateStr);
return isNaN(d.getTime()) ? new Date() : d;
};
// ✅ 主转换逻辑:生成三级嵌套任务结构
const transformedProjects = projects.map((project, projectIndex) => {
const projectId = projectIndex + 1; // 从 1 开始编号
return {
TaskID: projectId,
ProjectNo: String(project.projectNumber), // 强制转字符串,避免数字前导零丢失
TaskName: project.projectTitle || 'Untitled Project',
StartDate: safeDate(project.projectStart),
EndDate: safeDate(project.projectEnd),
subtasks: (project.workItems || []).map((workItem, workIndex) => {
const workId = `${projectId}.${workIndex + 1}`;
return {
TaskID: workId,
TaskName: workItem.name || 'Unnamed Work Item',
StartDate: safeDate(project.projectStart),
Duration: workItem.duration || 30, // 建议从数据中提取,此处设默认值
Progress: workItem.progress || 150,
comments: workItem.comments || '',
subtasks: (workItem.subtasks || []).map((subtask, subtaskIndex) => {
const subtaskId = `${workId}.${subtaskIndex + 1}`;
return {
TaskID: subtaskId,
TaskName: subtask.name || 'Unnamed Subtask',
StartDate: safeDate(project.projectStart),
Duration: subtask.duration || 30,
Progress: subtask.progress || 150,
};
}),
};
}),
};
});
关键要点说明:
-
层级 ID 生成:使用模板字符串拼接
projectId.workIndex.subtaskIndex,确保语义清晰且可排序(字符串自然排序即按层级顺序); -
空值防御:对
workItems和subtasks使用(arr || []).map(...),避免因字段缺失导致TypeError; -
日期鲁棒性:
safeDate()函数拦截非法日期字符串,防止整个转换流程崩溃; -
字段兜底:所有必填字段(如
TaskName、Duration)均提供默认值,提升数据容错能力; -
类型安全:
ProjectNo显式转为字符串,避免数字型项目号(如26278)在 JSON 序列化时丢失格式。
⚠️ 注意:若原始数据中
workItems或subtasks的日期字段不统一(如混用siteSurveyStart/designStart),需根据业务逻辑选择最合适的起始时间,而非硬编码projectStart。例如:StartDate: safeDate(workItem.siteSurveyStart || workItem.designStart || project.projectStart)
此方案完全满足示例中“1 → 1.1 → 1.11”的三级嵌套结构需求,且具备良好的可读性与可维护性,可直接集成至 React/Vue 等框架的数据预处理流程中。










