Add compact study and series sorting
This commit is contained in:
@@ -20,7 +20,10 @@ const app = {
|
||||
saving: false,
|
||||
pendingImage: null,
|
||||
drag: null,
|
||||
seriesSort: "timeAsc",
|
||||
studySortField: "time",
|
||||
studySortDir: "desc",
|
||||
seriesSortField: "time",
|
||||
seriesSortDir: "asc",
|
||||
showOverlay: true,
|
||||
thumbUrls: new Map(),
|
||||
thumbObserver: null,
|
||||
@@ -99,6 +102,16 @@ function timeKey(series) {
|
||||
return String(series.series_time || series.first_time || series.study_time || "").replace(/\D/g, "").padEnd(6, "0");
|
||||
}
|
||||
|
||||
function studyTimeKey(study) {
|
||||
const date = String(study.study_date || "").replace(/\D/g, "").padEnd(8, "0");
|
||||
const time = String(study.study_time || "").replace(/\D/g, "").slice(0, 6).padEnd(6, "0");
|
||||
return `${date}${time}`;
|
||||
}
|
||||
|
||||
function sortArrow(direction) {
|
||||
return direction === "asc" ? "↑" : "↓";
|
||||
}
|
||||
|
||||
function phaseLabel(value) {
|
||||
return { arterial: "动脉期", portal_venous: "门静脉期", delayed: "延迟期", unknown: "无法判别" }[value] || "";
|
||||
}
|
||||
@@ -185,10 +198,34 @@ function sortSeries(list) {
|
||||
if (skipA !== skipB) return skipA - skipB;
|
||||
const timeResult = timeKey(a).localeCompare(timeKey(b)) || String(a.series_number || "").localeCompare(String(b.series_number || ""), undefined, { numeric: true });
|
||||
const countResult = Number(a.count || 0) - Number(b.count || 0) || timeResult;
|
||||
if (app.seriesSort === "timeDesc") return -timeResult;
|
||||
if (app.seriesSort === "countAsc") return countResult;
|
||||
if (app.seriesSort === "countDesc") return -countResult;
|
||||
return timeResult;
|
||||
const result = app.seriesSortField === "count" ? countResult : timeResult;
|
||||
return app.seriesSortDir === "desc" ? -result : result;
|
||||
});
|
||||
}
|
||||
|
||||
function sortStudies(list) {
|
||||
return [...list].sort((a, b) => {
|
||||
const timeResult =
|
||||
studyTimeKey(a).localeCompare(studyTimeKey(b)) ||
|
||||
String(a.ct_number || "").localeCompare(String(b.ct_number || ""), undefined, { numeric: true });
|
||||
const unannotatedResult = Number(a.unannotated_series || 0) - Number(b.unannotated_series || 0) || timeResult;
|
||||
const result = app.studySortField === "unannotated" ? unannotatedResult : timeResult;
|
||||
return app.studySortDir === "desc" ? -result : result;
|
||||
});
|
||||
}
|
||||
|
||||
function updateSortButtons() {
|
||||
document.querySelectorAll("[data-study-sort]").forEach((button) => {
|
||||
const active = button.dataset.studySort === app.studySortField;
|
||||
button.classList.toggle("active", active);
|
||||
const arrow = button.querySelector(".sort-arrow");
|
||||
if (arrow) arrow.textContent = sortArrow(active ? app.studySortDir : "desc");
|
||||
});
|
||||
document.querySelectorAll("[data-series-sort]").forEach((button) => {
|
||||
const active = button.dataset.seriesSort === app.seriesSortField;
|
||||
button.classList.toggle("active", active);
|
||||
const arrow = button.querySelector(".sort-arrow");
|
||||
if (arrow) arrow.textContent = sortArrow(active ? app.seriesSortDir : "asc");
|
||||
});
|
||||
}
|
||||
|
||||
@@ -196,6 +233,7 @@ function setSeries(list) {
|
||||
app.series = sortSeries(list || []);
|
||||
$("seriesCount").textContent = String(app.series.length);
|
||||
renderSeries();
|
||||
updateSortButtons();
|
||||
}
|
||||
|
||||
function isSeriesAnnotated(series) {
|
||||
@@ -219,7 +257,7 @@ function refreshStudyAnnotationSummary() {
|
||||
|
||||
async function loadStudies() {
|
||||
const q = encodeURIComponent($("studySearch").value.trim());
|
||||
app.studies = await json(`/api/studies?q=${q}&limit=500`);
|
||||
app.studies = sortStudies(await json(`/api/studies?q=${q}&limit=500`));
|
||||
$("studyCount").textContent = String(app.studies.length);
|
||||
renderStudies();
|
||||
if (!app.study && app.studies.length) selectStudy(app.studies[0].ct_number);
|
||||
@@ -227,6 +265,8 @@ async function loadStudies() {
|
||||
|
||||
function renderStudies() {
|
||||
const list = $("studyList");
|
||||
app.studies = sortStudies(app.studies);
|
||||
updateSortButtons();
|
||||
list.innerHTML = "";
|
||||
for (const study of app.studies) {
|
||||
const button = document.createElement("button");
|
||||
@@ -901,16 +941,29 @@ function wireImageGestures() {
|
||||
});
|
||||
}
|
||||
|
||||
function setSort(direction) {
|
||||
app.seriesSort = direction;
|
||||
document.querySelectorAll("[data-sort]").forEach((button) => {
|
||||
button.classList.toggle("active", button.dataset.sort === direction);
|
||||
});
|
||||
function setStudySort(field) {
|
||||
if (app.studySortField === field) {
|
||||
app.studySortDir = app.studySortDir === "asc" ? "desc" : "asc";
|
||||
} else {
|
||||
app.studySortField = field;
|
||||
app.studySortDir = "desc";
|
||||
}
|
||||
renderStudies();
|
||||
}
|
||||
|
||||
function setSeriesSort(field) {
|
||||
if (app.seriesSortField === field) {
|
||||
app.seriesSortDir = app.seriesSortDir === "asc" ? "desc" : "asc";
|
||||
} else {
|
||||
app.seriesSortField = field;
|
||||
app.seriesSortDir = field === "count" ? "desc" : "asc";
|
||||
}
|
||||
app.series = sortSeries(app.series);
|
||||
renderSeries();
|
||||
if (app.activeSeries) {
|
||||
app.activeSeries = app.series.find((item) => item.series_uid === app.activeSeries.series_uid) || app.activeSeries;
|
||||
}
|
||||
updateSortButtons();
|
||||
}
|
||||
|
||||
function wire() {
|
||||
@@ -925,8 +978,11 @@ function wire() {
|
||||
app.showOverlay = !app.showOverlay;
|
||||
updateOverlay();
|
||||
});
|
||||
document.querySelectorAll("[data-sort]").forEach((button) => {
|
||||
button.addEventListener("click", () => setSort(button.dataset.sort));
|
||||
document.querySelectorAll("[data-study-sort]").forEach((button) => {
|
||||
button.addEventListener("click", () => setStudySort(button.dataset.studySort));
|
||||
});
|
||||
document.querySelectorAll("[data-series-sort]").forEach((button) => {
|
||||
button.addEventListener("click", () => setSeriesSort(button.dataset.seriesSort));
|
||||
});
|
||||
$("studySearch").addEventListener("input", () => {
|
||||
clearTimeout(app.searchTimer);
|
||||
|
||||
Reference in New Issue
Block a user