(function (global) { "use strict"; const ICONS = { "chevron-left": '', "chevron-right": '', "sun": '', "moon": '', "bar-chart-3": '', "wand-2": '', "notebook-pen": '', "message-square": '', "settings": '', "inbox": '', "activity": '', "trending-up": '', "trending-down": '', "zap": '', "history": '', "chart-line": '', "layers": '', "refresh-cw": '', "gavel": '', "book-open": '', "flame": '', "crown": '', "filter": '', "target": '', "bot": '', "star": '', "scroll-text": '', "calendar-check": '', "sticky-note": '', "bell": '', "user": '', "lock": '', "gem": '', "sliders-horizontal": '', "users": '' }; function icon(name, size) { const body = ICONS[name] || ""; return '"; } function escapeHtml(value) { return String(value == null ? "" : value).replace(/[&<>"']/g, function (ch) { return { "&": "&", "<": "<", ">": ">", '"': """, "'": "'" }[ch]; }); } const DEFAULT_HASH = "#/hub/market"; const stack = []; let internalNav = 0; let authMode = "login"; function currentHash() { let hash = window.location.hash || ""; if (!hash || hash === "#" || hash === "#/") return DEFAULT_HASH; if (hash.charAt(0) !== "#") hash = "#" + hash; return hash; } function routeOf(hash) { const path = hash.replace(/^#\/?/, ""); const parts = path.split("/").filter(Boolean); return { name: parts[0] || "home", params: parts.slice(1) }; } function currentRoute() { return routeOf(stack.length ? stack[stack.length - 1] : currentHash()); } function entryKeyOfRoute(route) { if (route.name === "hub") return route.params[0]; if (route.name === "feature") return route.params[0]; if (route.name === "assistant") return "assistant"; return null; } function entryRoute(key) { return key === "assistant" ? "#/assistant/chat" : "#/hub/" + key; } function setHash(hash) { internalNav++; window.location.hash = hash; } function navigate(hash) { stack.push(hash); setHash(hash); render(); } function replace(hash) { if (stack.length) stack[stack.length - 1] = hash; else stack.push(hash); setHash(hash); render(); } function resetStack(hash) { stack.length = 0; stack.push(hash); setHash(hash); render(); } function parentRoute(route) { if (route.name === "feature") return "#/hub/" + route.params[0]; return null; } function back() { if (stack.length > 1) { stack.pop(); setHash(stack[stack.length - 1]); render(); return; } const parent = parentRoute(currentRoute()); if (parent) replace(parent); } function switchEntry(key) { const route = currentRoute(); const current = entryKeyOfRoute(route); if (current === key) { if (route.name === "feature") { resetStack(entryRoute(key)); } return; } resetStack(entryRoute(key)); } function updateHeader(options) { document.getElementById("m-title").textContent = options.title || "小白复盘"; document.getElementById("m-back").hidden = !options.back; document.getElementById("m-actions").innerHTML = options.actions || ""; } function placeholderHtml(title, subtitle) { return '
' + '' + icon("inbox", 26) + "" + "

" + escapeHtml(title) + "

" + "

" + escapeHtml(subtitle || "该页面将在后续批次实现,返回即可继续浏览。") + "

" + "
"; } function findFeatureLabel(key) { const hubs = global.MobileNav.hubs; for (const hubKey in hubs) { const items = hubs[hubKey].items || []; for (const item of items) { if (item.key === key) return item.label; } } return null; } function themeToggleSection() { const dark = document.getElementById("m-app").dataset.theme === "dark"; return '
' + '
' + '' + icon(dark ? "moon" : "sun") + "" + '外观主题' + (dark ? "当前:夜间模式" : "当前:日间模式") + "" + '' + "
"; } function syncThemeToggleUI() { const dark = document.getElementById("m-app").dataset.theme === "dark"; const toggle = document.querySelector("[data-theme-toggle]"); if (toggle) toggle.setAttribute("aria-checked", dark ? "true" : "false"); const rowIcon = document.querySelector(".m-theme-row-icon"); if (rowIcon) rowIcon.innerHTML = icon(dark ? "moon" : "sun"); const rowBodySmall = document.querySelector(".m-theme-row-body small"); if (rowBodySmall) rowBodySmall.textContent = dark ? "当前:夜间模式" : "当前:日间模式"; } function visibleHubItems(hub) { const items = hub && hub.items ? hub.items : []; return items.filter(function (item) { return !item.adminOnly || global.MobileSession.isAdmin(); }); } function renderHub(key) { const hub = global.MobileNav.hubs[key]; if (!hub) { replace(DEFAULT_HASH); return; } const items = visibleHubItems(hub); updateHeader({ title: hub.title, back: false }); const section = key === "system" ? themeToggleSection() : ""; document.getElementById("m-view").innerHTML = section + '
    ' + items.map(function (item) { return '
  • ' + '
  • "; }).join("") + "
"; } function renderFeature(key) { const title = findFeatureLabel(key) || key; updateHeader({ title: title, back: true }); document.getElementById("m-view").innerHTML = placeholderHtml(title, "该功能页将在后续批次实现。"); } function renderAssistant() { updateHeader({ title: "复盘助手", back: false }); document.getElementById("m-view").innerHTML = placeholderHtml("复盘助手", "聊天工作台将在后续批次实现。"); } function renderAuth() { updateHeader({ title: "小白复盘", back: false, actions: "" }); document.getElementById("m-view").innerHTML = [ '
', '
', '', '

小白复盘

', '

登录后进入你的复盘空间

', "
", '
', '', '', "
", '
', '', '', '', '', '', "
", "
" ].join(""); authMode = "login"; bindAuth(); } function setAuthMode(mode) { authMode = mode === "register" ? "register" : "login"; document.querySelectorAll("[data-auth-mode]").forEach(function (button) { button.classList.toggle("active", button.dataset.authMode === authMode); }); document.getElementById("m-auth-confirm-field").hidden = authMode !== "register"; document.getElementById("m-auth-confirm").required = authMode === "register"; document.getElementById("m-auth-password").autocomplete = authMode === "register" ? "new-password" : "current-password"; document.getElementById("m-auth-submit").textContent = authMode === "register" ? "注册并进入" : "登录"; document.getElementById("m-auth-error").hidden = true; } function bindAuth() { document.querySelectorAll("[data-auth-mode]").forEach(function (button) { button.addEventListener("click", function () { setAuthMode(button.dataset.authMode); }); }); document.getElementById("m-auth-form").addEventListener("submit", submitAuth); } async function submitAuth(event) { event.preventDefault(); const username = document.getElementById("m-auth-username").value.trim(); const password = document.getElementById("m-auth-password").value; const errorElement = document.getElementById("m-auth-error"); if (authMode === "register" && password !== document.getElementById("m-auth-confirm").value) { errorElement.textContent = "两次输入的密码不一致。"; errorElement.hidden = false; return; } const submit = document.getElementById("m-auth-submit"); submit.disabled = true; try { if (authMode === "register") { await global.MobileSession.register(username, password); } else { await global.MobileSession.login(username, password); } replace(DEFAULT_HASH); } catch (error) { errorElement.textContent = error.message || "账号操作失败"; errorElement.hidden = false; } finally { submit.disabled = false; } } function buildTabbar() { document.getElementById("m-tabbar").innerHTML = global.MobileNav.entries.map(function (entry) { return '"; }).join(""); } function updateTabbar(activeKey) { document.querySelectorAll("#m-tabbar .m-tabbar-item").forEach(function (button) { const active = button.dataset.tab === activeKey; button.classList.toggle("active", active); if (active) button.setAttribute("aria-current", "page"); else button.removeAttribute("aria-current"); }); } function render() { const route = currentRoute(); if (route.name === "home") { replace(DEFAULT_HASH); return; } const tabbar = document.getElementById("m-tabbar"); if (route.name === "auth") { tabbar.hidden = true; document.getElementById("m-app").dataset.tabbar = "false"; } else { tabbar.hidden = false; document.getElementById("m-app").dataset.tabbar = "true"; updateTabbar(entryKeyOfRoute(route)); } if (route.name === "hub") renderHub(route.params[0]); else if (route.name === "feature") renderFeature(route.params.join("/")); else if (route.name === "assistant") renderAssistant(); else if (route.name === "auth") renderAuth(); } function bindGlobalEvents() { document.getElementById("m-view").addEventListener("click", function (event) { const entry = event.target.closest("[data-route]"); if (entry) { navigate(entry.dataset.route); } }); document.getElementById("m-tabbar").addEventListener("click", function (event) { const item = event.target.closest("[data-tab]"); if (item) switchEntry(item.dataset.tab); }); document.getElementById("m-back").addEventListener("click", back); document.addEventListener("click", function (event) { const toggle = event.target.closest("[data-theme-toggle]"); if (!toggle) return; global.MobileTheme.toggle(); syncThemeToggleUI(); }); window.addEventListener("hashchange", function () { if (internalNav > 0) { internalNav--; return; } const hash = currentHash(); if (stack.length > 1 && hash === stack[stack.length - 2]) { stack.pop(); } else if (hash !== stack[stack.length - 1]) { stack.push(hash); } render(); }); } function init() { bindGlobalEvents(); buildTabbar(); stack.length = 0; const raw = window.location.hash || ""; if (!raw || raw === "#" || raw === "#/") { stack.push(DEFAULT_HASH); setHash(DEFAULT_HASH); } else { stack.push(currentHash()); } render(); } global.MobileRouter = { init: init, navigate: navigate, replace: replace, back: back, render: render, currentRoute: currentRoute }; })(window);