From ad77a19fbd3316def0efadcdca75421e9f7d9cca Mon Sep 17 00:00:00 2001 From: Codex Date: Sat, 13 Jun 2026 23:26:17 +0800 Subject: [PATCH] feat: add date and time sorting controls --- app/static/app.js | 101 +++++++++++++++++++++++++++++++++++++++++- app/static/index.html | 12 +++-- app/static/styles.css | 26 +++++++++++ 3 files changed, 133 insertions(+), 6 deletions(-) diff --git a/app/static/app.js b/app/static/app.js index 91c3b82..2cc6104 100644 --- a/app/static/app.js +++ b/app/static/app.js @@ -4,6 +4,8 @@ const recordForm = document.querySelector("#recordForm"); const recordQuery = document.querySelector("#recordQuery"); const recordMeta = document.querySelector("#recordMeta"); const recordRows = document.querySelector("#recordRows"); +const dateSortBtn = document.querySelector("#dateSortBtn"); +const timeSortBtn = document.querySelector("#timeSortBtn"); const inlineAccount = document.querySelector("#inlineAccount"); const correctionToolbar = document.querySelector("#correctionToolbar"); const correctionStatus = document.querySelector("#correctionStatus"); @@ -23,7 +25,9 @@ const cancelCorrectionBtn = document.querySelector("#cancelCorrectionBtn"); const WEEKDAYS = ["星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"]; const COPY_LINE_BREAK = "\r\n"; +const SORT_DIRECTIONS = { asc: 1, desc: -1 }; let currentRecords = []; +let recordSort = { date: "asc", time: "asc" }; let currentRecordOrder = []; let correctedRecords = new Map(); let activeCorrectionKey = ""; @@ -76,10 +80,97 @@ function groupRecordsByTeacher(records) { }); } +function getDisplayRecord(row) { + return correctedRecords.get(row._recordKey) || row; +} + +function normalizeDateForSort(value) { + const text = String(value || ""); + const match = text.match(/^(\d{4})\.(\d{1,2})\.(\d{1,2})$/); + if (!match) return text; + return `${match[1]}.${match[2].padStart(2, "0")}.${match[3].padStart(2, "0")}`; +} + +function compareDatesForSort(a, b) { + return normalizeDateForSort(a).localeCompare(normalizeDateForSort(b), "zh-CN", { numeric: true }); +} + +function parseStartMinutes(value) { + const match = String(value || "").match(/^(\d{1,2}):(\d{2})/); + if (!match) return null; + return Number(match[1]) * 60 + Number(match[2]); +} + +function compareStartTimesForSort(a, b) { + const minutesA = parseStartMinutes(a); + const minutesB = parseStartMinutes(b); + if (minutesA !== null && minutesB !== null) return minutesA - minutesB; + if (minutesA !== null) return -1; + if (minutesB !== null) return 1; + return String(a || "").localeCompare(String(b || ""), "zh-CN", { numeric: true }); +} + +function compareRecordsForDisplay(a, b) { + const displayA = getDisplayRecord(a); + const displayB = getDisplayRecord(b); + const dateCompare = compareDatesForSort(displayA.date, displayB.date); + if (dateCompare !== 0) return dateCompare * SORT_DIRECTIONS[recordSort.date]; + + const timeCompare = compareStartTimesForSort(displayA.time, displayB.time); + if (timeCompare !== 0) return timeCompare * SORT_DIRECTIONS[recordSort.time]; + + return (a._recordIndex || 0) - (b._recordIndex || 0); +} + +function sortRecordsForDisplay(records) { + return [...records].sort(compareRecordsForDisplay); +} + +function sortDirectionLabel(direction) { + return direction === "asc" ? "升序排列" : "降序排列"; +} + +function sortDirectionName(direction) { + return sortDirectionLabel(direction); +} + +function nextSortDirectionName(direction) { + return direction === "asc" ? "降序排列" : "升序排列"; +} + +function updateRecordSortButtons() { + dateSortBtn.textContent = `日期 ${sortDirectionLabel(recordSort.date)}`; + dateSortBtn.setAttribute( + "aria-label", + `日期排序:${sortDirectionName(recordSort.date)},点击切换为${nextSortDirectionName(recordSort.date)}`, + ); + timeSortBtn.textContent = `时间 ${sortDirectionLabel(recordSort.time)}`; + timeSortBtn.setAttribute( + "aria-label", + `时间排序:${sortDirectionName(recordSort.time)},点击切换为${nextSortDirectionName(recordSort.time)}`, + ); +} + +function resetRecordSort() { + recordSort = { date: "asc", time: "asc" }; + updateRecordSortButtons(); +} + +function renderCurrentRecords() { + if (!currentRecords.length) return; + recordRows.innerHTML = renderGroupedRecords(currentRecords); +} + +function toggleRecordSort(field) { + recordSort = { ...recordSort, [field]: recordSort[field] === "asc" ? "desc" : "asc" }; + updateRecordSortButtons(); + renderCurrentRecords(); +} + function renderRecordRow(row) { const key = row._recordKey; const corrected = correctedRecords.get(key); - const displayRow = corrected || row; + const displayRow = getDisplayRecord(row); const correctedClass = corrected ? " corrected-row" : ""; const actionLabel = corrected ? "编辑" : "纠错"; const badge = corrected ? '已修改' : ""; @@ -110,7 +201,7 @@ function renderGroupedRecords(records) { ${group.count} 条记录 · ${fmtHours(group.totalHours)} 小时 - ${group.records + ${sortRecordsForDisplay(group.records) .map((row) => { currentRecordOrder.push(row._recordKey); return renderRecordRow(row); @@ -375,6 +466,7 @@ async function queryRecords(query) { recordMeta.innerHTML = ""; currentRecords = []; resetCorrections(); + resetRecordSort(); clearInlineAccount(); try { const data = await fetchJson(`/api/records?q=${encodeURIComponent(q)}&limit=500`); @@ -397,6 +489,7 @@ async function queryRecords(query) { currentRecords = data.records.map((row, index) => ({ ...row, + _recordIndex: index, _recordKey: makeRecordKey(row, index), })); recordRows.innerHTML = renderGroupedRecords(currentRecords); @@ -415,6 +508,9 @@ refreshBtn.addEventListener("click", () => { if (recordQuery.value.trim()) queryRecords(recordQuery.value); }); +dateSortBtn.addEventListener("click", () => toggleRecordSort("date")); +timeSortBtn.addEventListener("click", () => toggleRecordSort("time")); + document.querySelectorAll("[data-query]").forEach((button) => { button.addEventListener("click", () => { recordQuery.value = button.dataset.query; @@ -455,4 +551,5 @@ document.addEventListener("keydown", (event) => { copyCorrectedBtn.addEventListener("click", copyCorrectedRecords); +updateRecordSortButtons(); loadHealth(); diff --git a/app/static/index.html b/app/static/index.html index 17a317d..5d8bf24 100644 --- a/app/static/index.html +++ b/app/static/index.html @@ -4,7 +4,7 @@ 新时空课程记录查询 - +
@@ -50,8 +50,12 @@ - - + + @@ -112,6 +116,6 @@ - + diff --git a/app/static/styles.css b/app/static/styles.css index 6697d29..4088cac 100644 --- a/app/static/styles.css +++ b/app/static/styles.css @@ -358,6 +358,32 @@ th { font-weight: 700; } +.sort-header-button { + display: inline-flex; + align-items: center; + justify-content: flex-start; + min-width: 64px; + padding: 0; + border: 0; + background: transparent; + color: inherit; + cursor: pointer; + font: inherit; + font-weight: 700; + white-space: nowrap; +} + +.sort-header-button:hover, +.sort-header-button:focus-visible { + color: var(--accent-strong); +} + +.sort-header-button:focus-visible { + border-radius: 4px; + outline: 2px solid rgba(15, 118, 110, 0.35); + outline-offset: 3px; +} + td { font-size: 14px; }
日期时间 + + + + 学生 老师 科目