refactor: establish standalone application boundary

This commit is contained in:
leefer
2026-08-03 21:42:25 +08:00
parent cc5fb8d73e
commit e1e76cd51e
324 changed files with 63090 additions and 44743 deletions
+81
View File
@@ -0,0 +1,81 @@
function initializeAutoTableSorting() {
markAutoSortableHeaders(document);
document.addEventListener("click", (event) => {
const header = event.target.closest?.("th[data-auto-sort]");
if (!header || header.closest("#limitTable")) return;
const table = header.closest("table");
const body = table?.tBodies?.[0];
if (!body || body.rows.length < 2) return;
const direction = header.classList.contains("sort-asc") ? "desc" : "asc";
table.querySelectorAll("th.sort-asc, th.sort-desc").forEach((item) => {
item.classList.remove("sort-asc", "sort-desc", "sorted");
item.removeAttribute("aria-sort");
const arrow = item.querySelector(".arr");
if (arrow) arrow.textContent = "↕";
});
header.classList.add(`sort-${direction}`, "sorted");
header.setAttribute("aria-sort", direction === "asc" ? "ascending" : "descending");
const activeArrow = header.querySelector(".arr");
if (activeArrow) activeArrow.textContent = direction === "asc" ? "▲" : "▼";
const columnIndex = header.cellIndex;
const rows = [...body.rows].map((row, index) => ({ row, index }));
rows.sort((left, right) => {
const leftValue = autoSortValue(left.row.cells[columnIndex]);
const rightValue = autoSortValue(right.row.cells[columnIndex]);
let result;
if (leftValue.kind === "number" && rightValue.kind === "number") result = leftValue.value - rightValue.value;
else result = String(leftValue.value).localeCompare(String(rightValue.value), "zh-CN", { numeric: true, sensitivity: "base" });
if (result === 0) result = left.index - right.index;
return direction === "asc" ? result : -result;
});
rows.forEach(({ row }) => body.appendChild(row));
const firstHeader = [...header.parentElement.cells][0]?.textContent.trim();
if (["#", "排名"].includes(firstHeader)) {
[...body.rows].forEach((row, index) => {
if (row.cells[0]) row.cells[0].textContent = String(index + 1);
});
}
});
}
function markAutoSortableHeaders(root) {
root.querySelectorAll?.(".data-table:not(#limitTable) thead th").forEach((header) => {
if (header.closest("#brokenTable, #downTable, #yesterdayTable, #rotationTable")) return;
if (number(header.colSpan) > 1) return;
const label = header.textContent.trim();
if (!label || ["#", "操作"].includes(label)) return;
header.dataset.autoSort = "true";
header.classList.add("sortable");
if (!header.querySelector(".arr")) header.insertAdjacentHTML("beforeend", '<span class="arr">↕</span>');
header.title = `${label}:点击排序`;
});
}
function autoSortValue(cell) {
const text = String(cell?.dataset?.sortValue || cell?.textContent || "").trim();
if (!text || text === "--" || text.includes("样本不足")) return { kind: "text", value: "\uffff" };
const boardMatch = text.match(/(\d+)\s*板/);
if (boardMatch) return { kind: "number", value: Number(boardMatch[1]) };
const normalized = text.replaceAll(",", "").replace(/[+%]/g, "");
const numericMatch = normalized.match(/^-?\d+(?:\.\d+)?/);
if (numericMatch) {
let value = Number(numericMatch[0]);
if (text.includes("亿")) value *= 10000;
return { kind: "number", value };
}
return { kind: "text", value: text };
}
function bindSharedTableEvents() {
document.querySelectorAll("[data-table-search]").forEach((input) => {
input.addEventListener("input", () => {
const query = input.value.trim().toLowerCase();
const body = document.querySelector(`#${CSS.escape(input.dataset.tableSearch)}`);
body?.querySelectorAll("tr").forEach((row) => {
row.hidden = Boolean(query) && !row.textContent.toLowerCase().includes(query);
});
});
});
initializeAutoTableSorting();
}