fix: 概览耗时显示精确到时分 + 总耗时含加班

1. 开始日期/实际截止显示精确到分钟:
   - 开始:取所有阶段最早时间 slice(0,16)
   - 实际截止:取所有阶段最晚完成时间 slice(0,16)
   - 格式如"2025-06-15 14:30"

2. 总耗时包含加班时长:
   - 显示"已耗时 X 天(含加班 Yh)"
   - 加班时长从 overtime records 实时汇总
   - 无加班时不显示加班部分

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Script Generator
2026-06-15 15:59:04 +08:00
parent 24fb392017
commit 54efe53b11

View File

@@ -396,7 +396,7 @@ export default function VersionDetailPage() {
}); });
vDTs.forEach((t) => { if (t.startDate) startDates.push(t.startDate); }); vDTs.forEach((t) => { if (t.startDate) startDates.push(t.startDate); });
vTCsAll.forEach((c) => { if (c.startedAt) startDates.push(c.startedAt); }); vTCsAll.forEach((c) => { if (c.startedAt) startDates.push(c.startedAt); });
const actualStart = startDates.length > 0 ? startDates.sort()[0].slice(0, 10) : (version.startDate ?? null); const actualStart = startDates.length > 0 ? startDates.sort()[0].slice(0, 16).replace('T', ' ') : (version.startDate ?? null);
// 实际截止日期 // 实际截止日期
const endDates: string[] = []; const endDates: string[] = [];
@@ -404,15 +404,19 @@ export default function VersionDetailPage() {
vDTs.forEach((t) => { if (t.completedAt) endDates.push(t.completedAt); }); vDTs.forEach((t) => { if (t.completedAt) endDates.push(t.completedAt); });
vTCsAll.forEach((c) => { if (c.completedAt) endDates.push(c.completedAt); }); vTCsAll.forEach((c) => { if (c.completedAt) endDates.push(c.completedAt); });
vBugsAll.forEach((b) => { if (b.closedAt) endDates.push(b.closedAt); }); vBugsAll.forEach((b) => { if (b.closedAt) endDates.push(b.closedAt); });
const actualEnd = endDates.length > 0 ? endDates.sort().reverse()[0].slice(0, 10) : null; const actualEnd = endDates.length > 0 ? endDates.sort().reverse()[0].slice(0, 16).replace('T', ' ') : null;
// 逾期天数 // 逾期天数
const deadline = version.expectedReleaseDate; const deadline = version.expectedReleaseDate;
let overdueDays = 0; let overdueDays = 0;
if (deadline && actualEnd) { if (deadline && actualEnd) {
overdueDays = Math.floor((new Date(actualEnd).getTime() - new Date(deadline).getTime()) / (1000 * 60 * 60 * 24)); overdueDays = Math.floor((new Date(actualEnd.replace(' ', 'T')).getTime() - new Date(deadline).getTime()) / (1000 * 60 * 60 * 24));
} }
// 总耗时 = 日历天数 + 加班时长
const versionOTForTime = records.filter((r) => r.versionId === version.id);
const otHoursTotal = Math.round(versionOTForTime.reduce((sum, r) => sum + r.duration, 0) * 10) / 10;
return ( return (
<div className="flex flex-wrap items-center gap-x-4 gap-y-2 text-[13px]"> <div className="flex flex-wrap items-center gap-x-4 gap-y-2 text-[13px]">
<div className="flex items-center gap-1.5"> <div className="flex items-center gap-1.5">
@@ -436,6 +440,9 @@ export default function VersionDetailPage() {
<div className="flex items-center gap-1.5 text-[var(--ink-soft)]"> <div className="flex items-center gap-1.5 text-[var(--ink-soft)]">
<Clock className="h-3.5 w-3.5 text-[var(--ink-muted)]" /> <Clock className="h-3.5 w-3.5 text-[var(--ink-muted)]" />
<span className="font-medium text-[var(--ink)]">{elapsedDays}</span> <span className="font-medium text-[var(--ink)]">{elapsedDays}</span>
{otHoursTotal > 0 && (
<span className="text-[var(--ink-muted)]"> <span className="font-medium text-orange-600">{otHoursTotal}h</span></span>
)}
</div> </div>
</div> </div>
); );