feat: add date and time sorting controls
This commit is contained in:
+99
-2
@@ -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 ? '<span class="correction-badge">已修改</span>' : "";
|
||||
@@ -110,7 +201,7 @@ function renderGroupedRecords(records) {
|
||||
<span>${group.count} 条记录 · ${fmtHours(group.totalHours)} 小时</span>
|
||||
</div>
|
||||
</td>
|
||||
</tr>${group.records
|
||||
</tr>${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();
|
||||
|
||||
Reference in New Issue
Block a user