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", ''); 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(); }