摘要
Day 10 我們能依照時間、任務或原因來排序清單。
今天要進一步加入「篩選」功能(只顯示特定原因的紀錄),並補上排序與篩選的「多分頁同步」,讓不同分頁的排序/篩選偏好保持一致。
HTML:
<label for="filterSelect">篩選:</label>
<select id="filterSelect">
<option value="all">全部</option>
<option value="tired">只看「太累」</option>
<option value="mood">只看「沒心情」</option>
<option value="distractions">只看「太多干擾」</option>
<option value="difficult">只看「太難」</option>
<option value="dontKnow">只看「不知道怎麼開始」</option>
<option value="other">只看「其他」</option>
</select>
JavaScript:
// ===== 常數與兼容處理 =====
const FILTER_PREF_KEY = 'procrastinator_filter_pref';
let currentFilter = localStorage.getItem(FILTER_PREF_KEY) || 'all';
// ===== 狀態 =====
const filterSelect = document.getElementById('filterSelect');
// 篩選+排序
function applySortFilter(list) {
let arr = Array.isArray(list) ? list.slice() : [];
// 篩選
const f = (filterSelect?.value ?? currentFilter);
if (f && f !== 'all') {
arr = arr.filter(r => r.reasonCode === f); // reasonCode(例如 "tired", "mood"),表示下拉清單上的拖延原因
}
// 排序(沿用 Day 10 的 applySort)
const s = (sortSelect?.value ?? currentSort);
switch (s) {
case 'time_asc':
arr.sort((a, b) => a.createdAt - b.createdAt);
break;
case 'reason':
arr.sort((a, b) => (a.reason || '').localeCompare(b.reason || ''));
break;
case 'task':
arr.sort((a, b) => (a.task || '').localeCompare(b.task || ''));
break;
case 'time_desc':
default:
arr.sort((a, b) => b.createdAt - a.createdAt);
break;
}
return arr;
}
// ===== 渲染(新到舊),每筆帶 data-id (改動一列)=====
function renderHistory() {
const el = document.getElementById('historyList');
// 改用 applySortFilter 套用目前排序/篩選,原為 const records = readRecords().slice().sort((a,b) => b.createdAt - a.createdAt);
const records = applySortFilter(readRecords());
el.innerHTML = records.length
? records.map(r => `
<li>
[${new Date(r.createdAt).toLocaleString()}] ${r.task} — ${r.reason} |「${r.quote}」
<button class="btn-delete" data-id="${r.id}">刪除</button>
</li>
`).join('')
: '<li class="muted">尚無紀錄</li>';
// stack 有東西才啟用回復按鈕
undoBtn.disabled = deletedStack.length === 0;
}
// 篩選下拉事件
filterSelect?.addEventListener('change', (e) => {
currentFilter = e.target.value;
localStorage.setItem(FILTER_PREF_KEY, currentFilter);
renderHistory();
});
// 多分頁同步
window.addEventListener('storage', (e) => {
if (e.key === SORT_PREF_KEY) {
currentSort = localStorage.getItem(SORT_PREF_KEY) || 'time_desc';
if (sortSelect) sortSelect.value = currentSort;
renderHistory();
}
if (e.key === FILTER_PREF_KEY) {
currentFilter = localStorage.getItem(FILTER_PREF_KEY) || 'all';
if (filterSelect) filterSelect.value = currentFilter;
renderHistory();
}
});
// ===== 初次載入 =====
(function initFilterUI() {
if (filterSelect) filterSelect.value = currentFilter;
})();