# 项目阶段截止时间功能实施指南 ## ✅ 已完成的实施内容 ### 1. 前端类型定义 ✅ **文件位置**: `src/app/models/project-phase.model.ts` 包含内容: - `PhaseInfo` - 单个阶段信息接口 - `PhaseDeadlines` - 阶段截止时间集合接口 - `ProjectData` - Project.data字段类型定义 - `PHASE_INFO` - 阶段常量配置(建模/软装/渲染/后期) - `PHASE_STATUS_INFO` - 阶段状态信息 - `PHASE_PRIORITY_INFO` - 优先级信息 - 工具函数: - `generateDefaultPhaseDeadlines()` - 生成默认阶段截止时间 - `isPhaseDelayed()` - 检查阶段是否延期 - `getPhaseDaysRemaining()` - 获取阶段剩余天数 - `getPhaseProgress()` - 获取阶段进度百分比 ### 2. 项目时间轴组件更新 ✅ **文件位置**: - `src/app/pages/team-leader/project-timeline/project-timeline.ts` - `src/app/pages/team-leader/project-timeline/project-timeline.html` 更新内容: - ✅ 导入阶段类型定义和工具函数 - ✅ 扩展 `ProjectTimeline` 接口添加 `phaseDeadlines` 字段 - ✅ 添加 `TimelineEvent` 接口支持多种事件类型 - ✅ 新增 `getProjectEvents()` 方法统一获取所有事件(含阶段截止) - ✅ 新增阶段相关工具方法: - `getPhaseLabel()` - 获取阶段标签 - `getPhaseIcon()` - 获取阶段图标 - `getPhaseColor()` - 获取阶段颜色 - ✅ 模板更新: - 使用统一的事件标记循环显示所有事件 - 图例添加阶段截止时间说明(🎨建模/🪑软装/🖼️渲染/✨后期) ### 3. Dashboard组件数据传递 ✅ **文件位置**: `src/app/pages/team-leader/dashboard/dashboard.ts` 更新内容: - ✅ `convertToProjectTimeline()` 方法中读取 `project.data.phaseDeadlines` - ✅ 将阶段截止时间数据传递给时间轴组件 ### 4. Cloud Code数据迁移脚本 ✅ **文件位置**: `cloud/jobs/migrate-project-phase-deadlines.js` 功能: - ✅ `migrateProjectPhaseDeadlines` - 批量为现有项目添加阶段截止时间 - 支持干跑模式(`dryRun: true`) - 支持批量处理(`batchSize: 100`) - 根据项目deadline反推各阶段时间 - 详细的进度和统计信息 - ✅ `testProjectPhaseDeadlines` - 测试单个项目的阶段时间生成 ### 5. Cloud Code工具函数 ✅ **文件位置**: `cloud/utils/project-phase-utils.js` 功能: - ✅ `generatePhaseDeadlines()` - 生成阶段截止时间 - ✅ `getCompanyPhaseDurations()` - 获取公司级默认工期配置 - ✅ `updatePhaseStatus()` - 更新阶段状态 - ✅ `getCurrentPhase()` - 获取当前阶段 - ✅ `isPhaseDelayed()` - 检查是否延期 - ✅ `getPhaseDaysRemaining()` - 获取剩余天数 Cloud Function: - ✅ `generateProjectPhaseDeadlines` - 生成阶段截止时间 - ✅ `updateProjectPhaseStatus` - 更新阶段状态 ### 6. 设计方案文档 ✅ **文件位置**: `docs/schema/project-phase-deadlines-design.md` 包含完整的: - 数据结构设计 - JSON示例 - 前后端代码示例 - 可视化建议 - 实施步骤 --- ## 🚀 如何使用 ### 前端使用示例 #### 1. 在组件中使用类型定义 ```typescript 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); ``` #### 2. 在项目时间轴中展示 时间轴组件已自动支持阶段截止时间展示,只需确保传入的项目数据包含 `phaseDeadlines` 字段: ```typescript const projectData: ProjectTimeline = { projectId: 'xxx', projectName: '李总现代简约全案', // ... 其他字段 phaseDeadlines: { modeling: { deadline: new Date('2024-12-08'), status: 'in_progress', estimatedDays: 7 }, // ... 其他阶段 } }; ``` ### Cloud Code使用示例 #### 1. 数据迁移(为现有项目添加阶段时间) ```javascript // 干跑模式(只计算不保存) Parse.Cloud.startJob('migrateProjectPhaseDeadlines', { dryRun: true, batchSize: 50 }); // 正式迁移 Parse.Cloud.startJob('migrateProjectPhaseDeadlines', { dryRun: false, batchSize: 100 }); ``` #### 2. 创建项目时生成阶段截止时间 ```javascript // 在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 }); }); ``` #### 3. 更新阶段状态 ```javascript // 方式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() }); ``` #### 4. 设置公司默认工期 ```javascript // 在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 }); ``` --- ## 📊 数据结构说明 ### Project.data.phaseDeadlines 字段结构 ```json { "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 | 否 | 备注信息 | --- ## 🎨 可视化效果 ### 阶段图标和颜色 - 🎨 **建模** - 蓝色 (#2196F3) - 🪑 **软装** - 紫色 (#9C27B0) - 🖼️ **渲染** - 橙色 (#FF9800) - ✨ **后期** - 绿色 (#4CAF50) ### 时间轴展示 - 项目条形图按紧急程度显示颜色 - 阶段截止时间在时间轴上显示为彩色标记 - 悬停显示详细信息(阶段名称、截止时间) - 只显示今日线之后的未来事件 --- ## 🧪 测试建议 ### 1. 单元测试 ```typescript 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); }); }); ``` ### 2. E2E测试 1. 创建新项目,验证自动生成阶段截止时间 2. 切换到时间轴视图,验证阶段标记显示 3. 悬停阶段标记,验证工具提示信息 4. 更新阶段状态,验证UI更新 --- ## 📋 待办事项 ### 已完成 ✅ - [x] 创建前端类型定义文件 - [x] 更新项目时间轴组件 - [x] 创建数据迁移脚本 - [x] 创建工具函数 - [x] 编写使用文档 ### 可选增强功能 🔮 - [ ] 添加阶段管理界面(手动调整截止时间) - [ ] 阶段延期自动提醒功能 - [ ] 阶段进度追踪报表 - [ ] 支持自定义阶段(不限于4个阶段) - [ ] 阶段依赖关系管理 --- ## 🐛 故障排查 ### 问题1:时间轴没有显示阶段标记 **原因**:项目数据中没有 `phaseDeadlines` 字段 **解决方案**: 1. 运行数据迁移脚本为现有项目添加数据 2. 或手动为项目添加 `phaseDeadlines` 数据 ### 问题2:阶段时间计算不正确 **原因**:项目没有 `deadline` 字段 **解决方案**: 确保所有项目都设置了 `deadline` 字段 ### 问题3:时间轴性能问题 **原因**:项目数量过多 **解决方案**: 1. 使用筛选功能减少显示的项目数量 2. 考虑添加分页或虚拟滚动 --- ## 📞 技术支持 如有问题,请参考: 1. 设计方案文档:`docs/schema/project-phase-deadlines-design.md` 2. schemas.md数据范式文档:`rules/schemas.md` 3. 代码注释和类型定义 --- **最后更新**: 2024年11月6日 **版本**: 1.0.0