fix: 调研/产品/UI阶段耗时改为精确时间戳计算

- VersionPlan 新增 actualStartAt 字段
- updatePlan 流转到 in_progress 时自动记录 actualStartAt
- updatePlan 流转到 completed 时自动记录 completedAt(如未设)
- 概览阶段耗时改用 actualStartAt/completedAt 计算(不再用计划排期)
- 个人耗时汇总同步使用实际时间戳

所有阶段(调研/产品/UI/开发/测试)现在统一用精确时间戳算耗时。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Script Generator
2026-06-12 16:24:46 +08:00
parent c86e87f95b
commit da1f98968b
3 changed files with 27 additions and 7 deletions

View File

@@ -393,15 +393,24 @@ export default function VersionDetailPage() {
// 调研阶段
const researchPlans = versionPlans.filter((p) => p.type === 'research');
const researchCal = calcCalendar(researchPlans.map((p) => p.startTime), researchPlans.filter((p) => p.status === 'completed').map((p) => p.endTime));
const researchCal = calcCalendar(
researchPlans.filter((p) => p.actualStartAt).map((p) => p.actualStartAt!),
researchPlans.filter((p) => p.completedAt).map((p) => p.completedAt!),
);
// 产品方案阶段
const productPlans = versionPlans.filter((p) => p.type === 'product');
const productCal = calcCalendar(productPlans.map((p) => p.startTime), productPlans.filter((p) => p.status === 'completed').map((p) => p.endTime));
const productCal = calcCalendar(
productPlans.filter((p) => p.actualStartAt).map((p) => p.actualStartAt!),
productPlans.filter((p) => p.completedAt).map((p) => p.completedAt!),
);
// UI设计阶段
const uiPlans = versionPlans.filter((p) => p.type === 'ui');
const uiCal = calcCalendar(uiPlans.map((p) => p.startTime), uiPlans.filter((p) => p.status === 'completed').map((p) => p.endTime));
const uiCal = calcCalendar(
uiPlans.filter((p) => p.actualStartAt).map((p) => p.actualStartAt!),
uiPlans.filter((p) => p.completedAt).map((p) => p.completedAt!),
);
// 开发阶段
const devCal = calcCalendar(
@@ -434,9 +443,8 @@ export default function VersionDetailPage() {
// 调研/产品/UI 用 startTime→endTime 计算
versionPlans.forEach((p) => {
if (!p.owner || !p.startTime) return;
const end = p.status === 'completed' ? p.endTime : undefined;
const hours = calcActualHoursByDates(p.startTime.slice(0, 10), end?.slice(0, 10));
if (!p.owner || !p.actualStartAt) return;
const hours = calcActualHoursByDates(p.actualStartAt, p.completedAt);
addHours(p.owner, p.type === 'research' ? 'research' : p.type === 'product' ? 'product' : 'ui', hours);
});
versionDevTasks.forEach((t) => {