/** * 策略说明:三所 MD + 开仓检查清单 JSON. */ (function () { const page = document.getElementById("page-strategy"); if (!page) return; const tabsEl = document.getElementById("strategy-tabs"); const statusEl = document.getElementById("strategy-load-status"); const docBody = document.getElementById("strategy-doc-body"); const docSource = document.getElementById("strategy-doc-source"); const docCard = page.querySelector(".strategy-doc-card"); const checklistCard = page.querySelector(".strategy-checklist-card"); const checklistTitle = document.getElementById("strategy-checklist-title"); const checklistBody = document.getElementById("strategy-checklist-body"); const footnotesEl = document.getElementById("strategy-checklist-footnotes"); const btnPrintDoc = document.getElementById("strategy-btn-print-doc"); const btnPrintChecklist = document.getElementById("strategy-btn-print-checklist"); const btnDownload = document.getElementById("strategy-btn-download"); let activeKey = "binance"; let tabsMeta = []; let cache = {}; let bound = false; let heightSyncRaf = 0; async function apiFetch(url, opts) { const r = await fetch(url, { credentials: "same-origin", ...(opts || {}) }); const ct = (r.headers.get("content-type") || "").toLowerCase(); if (ct.includes("application/json")) { const data = await r.json(); if (!r.ok) throw new Error((data && data.msg) || r.statusText || "请求失败"); return data; } if (!r.ok) throw new Error(r.statusText || "请求失败"); return r; } function esc(s) { return String(s ?? "") .replace(/&/g, "&") .replace(//g, ">") .replace(/"/g, """); } function syncDocCardHeight() { if (!docCard || !checklistCard || window.matchMedia("(max-width: 960px)").matches) { if (docCard) docCard.style.height = ""; return; } docCard.style.height = `${checklistCard.offsetHeight}px`; } function scheduleHeightSync() { if (heightSyncRaf) cancelAnimationFrame(heightSyncRaf); heightSyncRaf = requestAnimationFrame(() => { heightSyncRaf = 0; syncDocCardHeight(); }); } function renderTabs() { if (!tabsEl) return; tabsEl.innerHTML = tabsMeta .map( (t) => `` ) .join(""); tabsEl.querySelectorAll(".strategy-tab").forEach((btn) => { btn.addEventListener("click", () => { const key = btn.getAttribute("data-key"); if (!key || key === activeKey) return; activeKey = key; renderTabs(); void loadExchange(key); }); }); } function renderChecklist(checklist) { const cl = checklist || {}; const title = cl.title || "开仓检查清单"; if (checklistTitle) checklistTitle.textContent = title; if (!checklistBody) return; const groups = cl.groups || []; if (!groups.length) { checklistBody.innerHTML = '

暂无检查清单

'; } else { checklistBody.innerHTML = groups .map((grp) => { const items = (grp.items || []) .map((item) => `
  • ${esc(item)}
  • `) .join(""); return `

    ${esc(grp.title || "")}

    `; }) .join(""); } if (footnotesEl) { const notes = cl.footnotes || []; footnotesEl.innerHTML = notes.map((n) => `
  • ${esc(n)}
  • `).join(""); footnotesEl.classList.toggle("hidden", !notes.length); } scheduleHeightSync(); } function renderPayload(data) { if (docBody) docBody.innerHTML = data.strategy_html || ""; if (docSource) { const ver = data.version ? ` · ${data.version}` : ""; docSource.textContent = `文档:${data.md_source || ""}${ver}`; } renderChecklist(data.checklist); scheduleHeightSync(); } async function loadExchange(key) { if (statusEl) statusEl.textContent = "加载中…"; try { let data = cache[key]; if (!data) { data = await apiFetch(`/api/strategy/${encodeURIComponent(key)}`); cache[key] = data; } renderPayload(data); if (statusEl) statusEl.textContent = ""; } catch (e) { if (statusEl) statusEl.textContent = String(e); if (docBody) docBody.innerHTML = ""; if (checklistBody) checklistBody.innerHTML = ""; scheduleHeightSync(); } } async function loadMeta() { const meta = await apiFetch("/api/strategy/meta"); tabsMeta = meta.exchanges || []; if (tabsMeta.length && !tabsMeta.some((t) => t.key === activeKey)) { activeKey = tabsMeta[0].key; } renderTabs(); } function printSection(mode) { const part = mode === "checklist" ? "checklist" : "doc"; const url = `/api/strategy/${encodeURIComponent(activeKey)}/print?part=${encodeURIComponent(part)}`; const w = window.open(url, "_blank", "noopener,noreferrer"); if (!w) { if (statusEl) statusEl.textContent = "请允许弹出窗口以打开打印预览"; } } function bindActions() { if (bound) return; bound = true; if (btnPrintDoc) btnPrintDoc.addEventListener("click", () => printSection("doc")); if (btnPrintChecklist) btnPrintChecklist.addEventListener("click", () => printSection("checklist")); if (btnDownload) { btnDownload.addEventListener("click", () => { window.location.href = `/api/strategy/${encodeURIComponent(activeKey)}/export`; }); } window.addEventListener("resize", scheduleHeightSync); } async function init() { bindActions(); try { await loadMeta(); await loadExchange(activeKey); } catch (e) { if (statusEl) statusEl.textContent = String(e); } } function destroy() { window.removeEventListener("resize", scheduleHeightSync); } window.hubStrategyPage = { init, destroy }; })();