在 Day 20 完成了 CRUD 的連動渲染後,接下來要處理「數據視覺化」與「時間統計」的功能,讓使用者一眼就能掌握各個任務的完成進度與累積花費時間。
calculateTaskProgress)計算該主任務下已完成(completed)的子任務比例:

統計每一個子任務歷史紀錄(records)中的分鐘數,並轉換成方便閱讀的字串格式(例如:2 天 3 小時 15 分鐘)。
// 計算主任務累積總分鐘數
function getMainTaskTotalMinutes(task) {
if (!task.subtasks) return 0;
return task.subtasks.reduce((total, sub) => {
const subTotal = sub.records ? sub.records.reduce((acc, r) => acc + r.durationMinutes, 0) : 0;
return total + subTotal;
}, 0);
}
// 分鐘數轉可讀字串 (例: 135 分鐘 -> 2 小時 15 分鐘)
function formatMinutesToReadableText(totalMinutes) {
if (totalMinutes === 0) return "0 分鐘";
const hours = Math.floor(totalMinutes / 60);
const mins = totalMinutes % 60;
return `${hours > 0 ? hours + ' 小時 ' : ''}${mins} 分鐘`;
}
計算好數據後,我們在產出 Accordion 的 HTML 時,將 Bootstrap 的 Progress Bar 與時間標籤動態帶入:
Bootstrap Progress Bar:利用內建的 .progress 與 .progress-bar,將計算出來的進度百分比賦予到 CSS width 屬性。當進度達 100% 時,亦可將樣式切換為綠色(bg-success)。
總計時間標記:在主任務標題旁顯示整體累積花費時間。
// Accordion Item
const progress = calculateTaskProgress(task);
const readableTotalTime = formatMinutesToReadableText(getMainTaskTotalMinutes(task));
const headerHTML = `
<div class="row w-100 align-items-center">
<div class="col-md-4">
<span class="fw-bold">${task.title}</span>
</div>
<div class="col-md-8 d-flex align-items-center">
<!-- Bootstrap 進度條 -->
<div class="progress me-3 w-100" style="height: 18px;">
<div class="progress-bar ${progress === 100 ? 'bg-success' : 'bg-primary'}"
style="width: ${progress}%">
${progress}%
</div>
</div>
<!-- 總花費時間 -->
<span class="text-nowrap text-secondary fs-sm">總計:${readableTotalTime}</span>
</div>
</div>
`;
除了主任務的整體統計外,當使用者點擊子任務內的「檢視計時與備註紀錄」時,會展開隱藏的子區塊(Collapse),列出該子任務每次計時的時間區段與紀錄備註,使用者可以視需求查看。
