接下來我們要處理任務的狀態(未開始、進行中、已完成)。
使用者建立任務時,會自動分類成「未開始」;當該子任務有計時紀錄時,會自動轉為「進行中」;只有已完成是需要使用者自己勾選。
所以這篇將實現透過使用者的 「計時行為」 與 「Modal 勾選」 ,讓任務狀態自動且精確地切換。
1. 未開始:剛建立的任務,完全沒有任何專注計時紀錄。
2. 進行中:只要該任務產生過至少一次計時紀錄,系統即自動將狀態提升為「進行中」。
3. 已完成:使用者在「儲存計時 Modal」中手動勾選完成,或在任務列表中手動切換。
為了支援狀態追蹤,在子任務物件中新增 status 欄位(預設為 'not_started'),並配合 logs 紀錄歷程:
let tasks = [
{
id: "task-1",
title: "iThome鐵人賽",
subtasks: [
{ id: "sub-1", title: "主題發想", status: "completed" },
{ id: "sub-2", title: "撰寫每日文章", status: "in_progress" },
{ id: "sub-3", title: "mockup製作", status: "not_started" }
]
}
];
當使用者完成一次計時並點擊「儲存」時,saveSession() 會依據 Modal 上的勾選狀況,自動更新子任務的狀態:
// 儲存計時紀錄時自動流轉狀態
function saveSession() {
const currentTask = tasks.find((t) => t.id === currentMainTaskId);
const currentSub = currentTask?.subtasks.find((s) => s.id === currentSubtaskId);
if (currentSub) {
// 邏輯 1:如果 Modal 勾選已完成 -> 標記為 completed
if (modalIsCompleted.checked) {
currentSub.status = "completed";
}
// 邏輯 2:若原本是未開始,但產生了計時紀錄 -> 自動更改為 in_progress
else if (currentSub.status === "not_started") {
currentSub.status = "in_progress";
}
}
// 儲存紀錄邏輯 (略...)
saveTimerModal.hide();
resetTimerUI();
showToast("已成功儲存本次計時!");
renderAll(); // 觸發畫面更新,同步渲染最新狀態 Badge
}
除了子任務自身的狀態切換外,主任務的總體進度與狀態也是因應其下的子任務而變化,因此主任務的狀態不寫死在資料結構中,而是在渲染時動態計算。
當所有子任務都標記為 completed 時,主任務自動顯示為全數完成。
// 計算主任務的自動狀態 (自動推導)
function getMainTaskStatus(task) {
if (!task.subtasks || task.subtasks.length === 0) return "not_started";
const allCompleted = task.subtasks.every((s) => s.status === "completed");
if (allCompleted) return "completed";
const hasStarted = task.subtasks.some((s) => s.status !== "not_started");
if (hasStarted) return "in_progress";
return "not_started";
}
根據狀態呈現不同樣式的 Bootstrap Badge 標籤:
// 狀態標籤 UI 對照表
const STATUS_MAP = {
not_started: { label: "未開始", class: "bg-secondary-subtle text-secondary" },
in_progress: { label: "進行中", class: "bg-primary-subtle text-primary" },
completed: { label: "已完成", class: "bg-success-subtle text-success" }
};
// 渲染子任務時帶入狀態標籤
function renderSubtaskList() {
// ...過濾出當前子任務
const badge = STATUS_MAP[sub.status || "not_started"];
return `
<li class="d-flex justify-content-between align-items-center p-2">
<span>${sub.title}</span>
<span class="badge ${badge.class}">${badge.label}</span>
</li>
`;
}

1. 減少手動操作:
使用者按下停止並儲存計時的那一刻,「進行中」的狀態轉移就已經自動完成,不需要使用者手動切換。
2. 推導狀態優於冗餘資料:
主任務的狀態由子任務動態算出(every / some 陣列方法),能避免子任務全做完了,主任務狀態卻忘記更新的資料同步 Bug。
3. 視覺反饋:
透過 Bootstrap 5 的不同標籤顏色,讓使用者一開啟任務清單就能對整體進度一目了然。