Add hub usage help page with settings nav toggle.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-07-11 10:02:07 +08:00
parent 843d87422d
commit 9f0a30a040
12 changed files with 484 additions and 0 deletions
+76
View File
@@ -8652,6 +8652,82 @@ body.funds-fullscreen-open {
}
}
/* ── 使用说明 ── */
.help-toolbar {
margin-bottom: 12px;
min-height: 1.2em;
}
.help-layout {
display: grid;
grid-template-columns: minmax(180px, 220px) minmax(0, 1fr);
gap: 16px;
align-items: start;
}
.help-toc-card {
padding: 16px 14px 18px;
position: sticky;
top: 72px;
}
.help-toc-title {
margin: 0 0 12px;
font-size: 0.82rem;
font-weight: 700;
letter-spacing: 0.04em;
color: var(--text-soft);
text-transform: uppercase;
}
.help-toc-nav {
display: flex;
flex-direction: column;
gap: 4px;
}
.help-toc-item {
display: block;
width: 100%;
text-align: left;
padding: 8px 10px;
border: none;
border-radius: 8px;
background: transparent;
color: var(--text-soft);
font-size: 0.88rem;
cursor: pointer;
transition: background 0.15s, color 0.15s;
}
.help-toc-item:hover {
background: var(--surface-2, rgba(255, 255, 255, 0.04));
color: var(--text);
}
.help-toc-item.is-active {
background: var(--accent-soft, rgba(59, 130, 246, 0.12));
color: var(--accent, #3b82f6);
font-weight: 600;
}
.help-doc-card {
padding: 18px 20px 20px;
min-width: 0;
}
@media (max-width: 960px) {
.help-layout {
grid-template-columns: 1fr;
}
.help-toc-card {
position: static;
}
.help-toc-nav {
flex-direction: row;
flex-wrap: wrap;
}
.help-toc-item {
width: auto;
flex: 1 1 auto;
}
.help-doc-card .strategy-doc-body {
max-height: none;
}
}
/* ── 监控页:永续 / 期权分块 ── */
.hub-monitor-block {
margin-bottom: 4px;
+18
View File
@@ -41,6 +41,10 @@
return displayPref("show_nav_strategy", true);
}
function showNavHelpPref() {
return displayPref("show_nav_help", true);
}
function showNavLogsPref() {
return displayPref("show_nav_logs", true);
}
@@ -54,6 +58,7 @@
const navAi = document.getElementById("nav-ai");
const navCalc = document.getElementById("nav-calculator");
const navStrategy = document.getElementById("nav-strategy");
const navHelp = document.getElementById("nav-help");
const navLogs = document.getElementById("nav-logs");
if (navFunds) navFunds.classList.toggle("nav-hidden", d.show_nav_funds === false);
if (navDash) navDash.classList.toggle("nav-hidden", d.show_nav_dashboard === false);
@@ -62,6 +67,7 @@
if (navAi) navAi.classList.toggle("nav-hidden", d.show_nav_ai === false);
if (navCalc) navCalc.classList.toggle("nav-hidden", d.show_nav_calculator === false);
if (navStrategy) navStrategy.classList.toggle("nav-hidden", d.show_nav_strategy === false);
if (navHelp) navHelp.classList.toggle("nav-hidden", d.show_nav_help === false);
if (navLogs) navLogs.classList.toggle("nav-hidden", d.show_nav_logs === false);
}
@@ -73,6 +79,7 @@
if (page === "ai") return showNavAiPref();
if (page === "calculator") return showNavCalculatorPref();
if (page === "strategy") return showNavStrategyPref();
if (page === "help") return showNavHelpPref();
if (page === "logs") return showNavLogsPref();
return true;
}
@@ -87,6 +94,7 @@
const aiCb = document.getElementById("pref-show-nav-ai");
const calcCb = document.getElementById("pref-show-nav-calculator");
const strategyCb = document.getElementById("pref-show-nav-strategy");
const helpCb = document.getElementById("pref-show-nav-help");
const logsCb = document.getElementById("pref-show-nav-logs");
if (pnlCb) pnlCb.checked = d.show_account_pnl !== false;
if (fundsCb) fundsCb.checked = d.show_nav_funds !== false;
@@ -96,6 +104,7 @@
if (aiCb) aiCb.checked = d.show_nav_ai !== false;
if (calcCb) calcCb.checked = d.show_nav_calculator !== false;
if (strategyCb) strategyCb.checked = d.show_nav_strategy !== false;
if (helpCb) helpCb.checked = d.show_nav_help !== false;
if (logsCb) logsCb.checked = d.show_nav_logs !== false;
syncNavVisibility(data);
}
@@ -1208,6 +1217,7 @@
if (p.includes("funds")) return "funds";
if (p.includes("plan")) return "plan";
if (p.includes("calculator")) return "calculator";
if (p.includes("help")) return "help";
if (p.includes("strategy")) return "strategy";
if (p.includes("logs")) return "logs";
if (p.includes("market")) return "market";
@@ -1222,6 +1232,7 @@
if (page === "funds") return "page-funds";
if (page === "plan") return "page-plan";
if (page === "calculator") return "page-calculator";
if (page === "help") return "page-help";
if (page === "strategy") return "page-strategy";
if (page === "logs") return "page-logs";
if (page === "market") return "page-market";
@@ -1283,6 +1294,11 @@
} else if (window.hubStrategyPage && window.hubStrategyPage.destroy) {
window.hubStrategyPage.destroy();
}
if (page === "help" && window.hubHelpPage) {
window.hubHelpPage.init();
} else if (window.hubHelpPage && window.hubHelpPage.destroy) {
window.hubHelpPage.destroy();
}
if (page === "logs" && window.hubLogsPage) {
window.hubLogsPage.init();
} else if (window.hubLogsPage && window.hubLogsPage.destroy) {
@@ -4706,6 +4722,7 @@
const aiCb = document.getElementById("pref-show-nav-ai");
const calcCb = document.getElementById("pref-show-nav-calculator");
const strategyCb = document.getElementById("pref-show-nav-strategy");
const helpCb = document.getElementById("pref-show-nav-help");
const logsCb = document.getElementById("pref-show-nav-logs");
const supEnabled = document.getElementById("supervisor-enabled");
const supProg = document.getElementById("supervisor-wechat-program");
@@ -4727,6 +4744,7 @@
show_nav_ai: aiCb ? !!aiCb.checked : true,
show_nav_calculator: calcCb ? !!calcCb.checked : true,
show_nav_strategy: strategyCb ? !!strategyCb.checked : true,
show_nav_help: helpCb ? !!helpCb.checked : true,
show_nav_logs: logsCb ? !!logsCb.checked : true,
},
supervisor: {
+125
View File
@@ -0,0 +1,125 @@
/**
* 使用说明:中控 docs/help MD 章节.
*/
(function () {
const page = document.getElementById("page-help");
if (!page) return;
const tocEl = document.getElementById("help-toc-nav");
const statusEl = document.getElementById("help-load-status");
const docBody = document.getElementById("help-doc-body");
const docSource = document.getElementById("help-doc-source");
let sectionsMeta = [];
let activeKey = "quickstart";
let cache = {};
let bound = false;
async function apiFetch(url) {
const r = await fetch(url, { credentials: "same-origin" });
const data = await r.json();
if (!r.ok || !data.ok) throw new Error((data && data.msg) || r.statusText || "请求失败");
return data;
}
function esc(s) {
return String(s ?? "")
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;");
}
function sectionFromHash() {
const h = (window.location.hash || "").replace(/^#/, "").trim().toLowerCase();
if (!h) return null;
return sectionsMeta.some((s) => s.key === h) ? h : null;
}
function setHash(key) {
const next = `#${key}`;
if (window.location.hash !== next) {
history.replaceState(null, "", `/help${next}`);
}
}
function renderToc() {
if (!tocEl) return;
tocEl.innerHTML = sectionsMeta
.map(
(s) =>
`<button type="button" class="help-toc-item${s.key === activeKey ? " is-active" : ""}" data-key="${esc(s.key)}">${esc(s.label)}</button>`
)
.join("");
tocEl.querySelectorAll(".help-toc-item").forEach((btn) => {
btn.addEventListener("click", () => {
const key = btn.getAttribute("data-key");
if (!key || key === activeKey) return;
activeKey = key;
setHash(key);
renderToc();
void loadSection(key);
});
});
}
function renderSection(data) {
if (docBody) docBody.innerHTML = data.content_html || "";
if (docSource) {
docSource.textContent = data.md_source ? `来源: ${data.md_source}` : "";
}
if (statusEl) statusEl.textContent = "";
}
async function loadSection(key) {
if (cache[key]) {
renderSection(cache[key]);
return;
}
if (statusEl) statusEl.textContent = "加载中…";
try {
const data = await apiFetch(`/api/help/${encodeURIComponent(key)}`);
cache[key] = data;
renderSection(data);
} catch (err) {
if (statusEl) statusEl.textContent = "";
if (docBody) docBody.innerHTML = `<p class="muted">${esc(err.message || "加载失败")}</p>`;
}
}
async function loadMeta() {
const data = await apiFetch("/api/help/meta");
sectionsMeta = data.sections || [];
const fromHash = sectionFromHash();
if (fromHash) activeKey = fromHash;
else if (sectionsMeta.length && !sectionsMeta.some((s) => s.key === activeKey)) {
activeKey = sectionsMeta[0].key;
}
renderToc();
await loadSection(activeKey);
if (!sectionFromHash() && activeKey) setHash(activeKey);
}
function bindOnce() {
if (bound) return;
bound = true;
window.addEventListener("hashchange", () => {
const key = sectionFromHash();
if (!key || key === activeKey) return;
activeKey = key;
renderToc();
void loadSection(key);
});
}
window.hubHelpPage = {
init() {
bindOnce();
void loadMeta().catch((err) => {
if (statusEl) statusEl.textContent = "";
if (docBody) docBody.innerHTML = `<p class="muted">${esc(err.message || "加载失败")}</p>`;
});
},
destroy() {},
};
})();
+28
View File
@@ -52,6 +52,7 @@
<a href="/plan" id="nav-plan">开仓计划</a>
<a href="/monitor" id="nav-monitor">监控区</a>
<a href="/strategy" id="nav-strategy">策略说明</a>
<a href="/help" id="nav-help">使用说明</a>
<a href="/market" id="nav-market">行情区</a>
<a href="/calculator" id="nav-calculator">计算器</a>
<a href="/archive" id="nav-archive">内照明心</a>
@@ -890,6 +891,28 @@
</div>
</div>
<div id="page-help" class="page hidden">
<div class="page-head">
<div>
<h1><span class="head-tag">HLP</span> 使用说明</h1>
<p class="page-desc">中控与实例 · 日常操作指南(不含部署说明)</p>
</div>
</div>
<div class="help-toolbar">
<span id="help-load-status" class="toolbar-meta"></span>
</div>
<div class="help-layout">
<aside class="card help-toc-card">
<h3 class="help-toc-title">目录</h3>
<nav id="help-toc-nav" class="help-toc-nav" aria-label="使用说明目录"></nav>
</aside>
<section class="card help-doc-card">
<div id="help-doc-body" class="strategy-doc-body prose"></div>
<p id="help-doc-source" class="strategy-doc-source"></p>
</section>
</div>
</div>
<div id="page-logs" class="page hidden">
<div class="page-head hub-logs-page-head">
<div>
@@ -1010,6 +1033,10 @@
<input type="checkbox" id="pref-show-nav-strategy" checked />
顶栏显示「策略说明」
</label>
<label class="chk-label settings-display-chk">
<input type="checkbox" id="pref-show-nav-help" checked />
顶栏显示「使用说明」
</label>
<label class="chk-label settings-display-chk">
<input type="checkbox" id="pref-show-nav-logs" checked />
顶栏显示「系统日志」
@@ -1233,6 +1260,7 @@
<script src="/assets/funds.js?v=20260707-hub-options-funds"></script>
<script src="/assets/dashboard.js?v=20260708-options-expiry-cd"></script>
<script src="/assets/strategy.js?v=3"></script>
<script src="/assets/help.js?v=1"></script>
<script src="/assets/logs.js?v=1"></script>
<script src="/assets/ai_review_render.js?v=3"></script>
<script src="/assets/time_close_ui.js?v=3"></script>