window.XiaobaiPageModules.register("rotation", ["rotationView"], { enter: ["loadRotation"], }); /* PRESERVATION-SOURCE-BEGIN app.js:1958-2124 */ async function loadRotationHistory(force = false) { if (!state.dashboard || state.rotationLoading) return; const key = `${elements.tradeDate.value}:9`; if (!force && state.rotationHistoryKey === key && state.rotationHistory) { renderRotationHistory(); return; } state.rotationLoading = true; const container = document.querySelector("#rotationHistory"); renderEmptyState(container, "正在读取轮动历史"); try { const query = new URLSearchParams({ trade_date: elements.tradeDate.value, }); state.rotationHistory = await apiRequest(`/api/rotation/history?${query}`); state.rotationHistoryKey = key; renderRotationHistory(); } catch (error) { renderEmptyState(container, error.message || "轮动历史加载失败"); showToast(error.message || "轮动历史加载失败"); } finally { state.rotationLoading = false; } } function renderRotationHistory() { const rows = state.rotationHistory?.rows || []; const selected = state.rotationSelectedSector; const container = document.querySelector("#rotationHistory"); const tracker = document.querySelector("#rotationTracker"); if (!rows.length) { renderEmptyState(container, "尚无连续交易日的板块数据"); setText("rotationHistoryRange", "暂无轮动历史"); tracker.hidden = true; return; } const chronological = [...rows] .sort((left, right) => String(left.trade_date).localeCompare(String(right.trade_date))) .slice(-9); const displayRows = state.rotationOrder === "latest" ? [...chronological].reverse() : chronological; document.querySelectorAll("[data-rotation-order]").forEach((button) => { button.classList.toggle("active", button.dataset.rotationOrder === state.rotationOrder); }); setText( "rotationHistoryRange", `最近 ${chronological.length} 个交易日 · ${displayCompactDate(chronological[0].trade_date)} → ${displayCompactDate(chronological[chronological.length - 1].trade_date)} · ${state.rotationOrder === "latest" ? "由近到远,左侧为最新交易日" : "由远到近,右侧为最新交易日"}`, ); setText("rotationSelectionHint", selected ? `已联动高亮 ${selected}` : "点击任意板块追踪其连续性"); if (selected) { const sequence = displayRows.map((day) => { const sector = (day.sectors || []).find((item) => item.name === selected); return { tradeDate: day.trade_date, sector }; }); const appearances = sequence.filter((item) => item.sector); const bestRank = appearances.length ? Math.min(...appearances.map((item) => number(item.sector.rank))) : 0; tracker.hidden = false; const continuity = appearances.length >= 3 ? "主线候选" : appearances.length === 1 ? "单日异动,持续性待验证" : "间断活跃"; tracker.innerHTML = `
${escapeHtml(selected)}近 9 日在榜 ${appearances.length} 天 · 最高排名 #${bestRank || "--"} · ${continuity}
${sequence.map((item) => item.sector ? `#${number(item.sector.rank)}` : `--`).join("")}
`; tracker.querySelector(".rotation-track-cancel").addEventListener("click", () => { state.rotationSelectedSector = ""; state.rotationSelectedDate = ""; renderRotationHistory(); loadRotationMembers(""); }); } else { tracker.hidden = true; tracker.innerHTML = ""; } container.classList.toggle("tracking", Boolean(selected)); const latestTradeDate = chronological[chronological.length - 1].trade_date; container.innerHTML = displayRows.map((day) => { const hasSelected = selected && (day.sectors || []).some((sector) => sector.name === selected); return `
${(day.sectors || []).length} 个热点
${(day.sectors || []).map((sector) => { const strength = clamp(number(sector.strength), 0, 100); const heatClass = strength >= 90 ? "heat-strong" : strength >= 70 ? "heat-warm" : "heat-mild"; return ` `; }).join("")}
`; }).join(""); container.querySelectorAll("[data-rotation-sector]").forEach((button) => { button.addEventListener("click", () => { const clickedSector = button.dataset.rotationSector; const clickedDate = button.dataset.rotationDate; const isSameSelection = clickedSector === state.rotationSelectedSector && clickedDate === state.rotationSelectedDate; state.rotationSelectedSector = isSameSelection ? "" : clickedSector; state.rotationSelectedDate = isSameSelection ? "" : clickedDate; renderRotationHistory(); loadRotationMembers(state.rotationSelectedSector); }); }); } async function loadRotationMembers(sector, force = false) { if (!sector) { state.rotationMembers = null; state.rotationMembersKey = ""; renderRotationMembers(); return; } const memberDate = state.rotationSelectedDate || elements.tradeDate.value; const key = `${memberDate}:${sector}`; if (!force && state.rotationMembersKey === key && state.rotationMembers) { renderRotationMembers(); return; } state.rotationMembersLoading = true; renderRotationMembers(); try { const query = new URLSearchParams({ trade_date: memberDate, sector }); state.rotationMembers = await apiRequest(`/api/rotation/members?${query}`); state.rotationMembersKey = key; } catch (error) { state.rotationMembers = { error: error.message || "成分股加载失败", rows: [] }; state.rotationMembersKey = key; } finally { state.rotationMembersLoading = false; renderRotationMembers(); } } function renderRotationMembers() { const body = document.querySelector("#rotationTableBody"); const empty = document.querySelector("#rotationMembersEmpty"); if (state.rotationMembersLoading) { body.innerHTML = ""; empty.textContent = `正在核验${state.rotationSelectedSector}成分股`; empty.hidden = false; return; } const payload = state.rotationMembers; const rows = payload?.rows || []; if (!state.rotationSelectedSector || !payload || payload.error || !rows.length) { body.innerHTML = ""; empty.textContent = payload?.error || (state.rotationSelectedSector ? "该板块暂无可用成分行情" : "点击上方任意板块查看成分股"); empty.hidden = false; setText("rotationDetailTitle", "板块成分股"); setText("rotationDetailMeta", state.rotationSelectedSector || "--"); return; } empty.hidden = true; setText("rotationDetailTitle", `${payload.meta?.sector_name || state.rotationSelectedSector}成分股`); setText("rotationDetailMeta", `${displayCompactDate(payload.meta?.trade_date)} · ${number(payload.meta?.quoted_count)} / ${number(payload.meta?.member_count)} 只`); body.innerHTML = rows.map((row, index) => ` ${index + 1}${escapeHtml(row.code)}${escapeHtml(row.name)} ${row.quoted ? signed(row.change) : ""} ${row.quoted ? formatNumber(row.open, 2) : ""}${row.quoted ? formatNumber(row.close, 2) : ""} ${row.quoted ? formatNumber(row.amount_billion, 2) : ""}${row.quoted ? "正常交易" : "当日无行情"} `).join(""); animateRows(body); bindStockRows(body); } /* PRESERVATION-SOURCE-END app.js:1958-2124 */