diff --git a/app.py b/app.py index 1ad4dee..ae1d21c 100644 --- a/app.py +++ b/app.py @@ -20,7 +20,7 @@ import sqlite3 from datetime import timedelta import requests -from flask import Flask, render_template, request, jsonify, send_from_directory +from flask import Flask, render_template, request, jsonify, send_from_directory, redirect # ---- 初始化 Flask 应用 ---- app = Flask(__name__) @@ -257,17 +257,8 @@ def load_seed_videos(): @app.route("/") def index(): - """ - 主页路由 — 返回 SPA(单页应用)的 HTML。 - 前端所有页面切换都在这一个 HTML 里完成,不刷新浏览器。 - """ - return render_template("index.html") - - -@app.route("/demo") -def demo(): - """返回独立静态 Demo 页面""" - return send_from_directory(BASE_DIR, "mathbrain-demo.html") + """主页路由 — 重定向到桌面端网页版""" + return redirect("/web", code=302) @app.route("/web") diff --git a/data/miniapp_v3.db b/data/miniapp_v3.db index 2d317c0..7bc2167 100644 Binary files a/data/miniapp_v3.db and b/data/miniapp_v3.db differ diff --git a/static/css/style.css b/static/css/style.css deleted file mode 100644 index 3f120d6..0000000 --- a/static/css/style.css +++ /dev/null @@ -1,2249 +0,0 @@ -/* ========================================================= - style.css — 数学脑 v8.0 · 莫兰迪高保真 UI - 设计方向:极简 / 现代 / 干净 - 色彩方案:Morandi Palette(低饱和、哑光质感) - 排版层级清晰,微阴影,圆角设计 - ========================================================= */ - -:root { - /* ═══ 莫兰迪色系 (Morandi Palette) ═══ */ - --morandi-bg: #F0F2F5; /* 极浅燕麦色/米灰 — 全局背景 */ - --morandi-card: #FAFBFC; /* 极淡冷灰白 — 卡片底色 */ - --morandi-card-warm: #F5F0E6; /* 淡米黄 — 卡片杂色变体 */ - --morandi-card-cool: #EEF2F0; /* 淡灰绿 — 卡片杂色变体 */ - --morandi-card-lilac:#F2EEF5; /* 淡灰紫 — 卡片杂色变体 */ - - --morandi-blue: #A0B9D8; /* 灰雾蓝 — 主色调 */ - --morandi-blue-deep: #8AA3C2; /* 深雾蓝 — hover/active */ - --morandi-blue-light:#DCE6F0; /* 极淡雾蓝 — 浅色背景 */ - --morandi-pink: #DAB8B0; /* 豆沙粉 — 辅助色 */ - --morandi-pink-light:#F2E4DF; /* 极淡豆沙粉 */ - --morandi-green: #B8D0C9; /* 灰豆绿 — 辅助色 */ - --morandi-green-light:#E4EFEB; /* 极淡灰豆绿 */ - --morandi-purple: #C5CAD0; /* 灰紫 — 个人中心 */ - --morandi-purple-deep:#AEB3B9; - - --morandi-text: #4A4A4A; /* 深岩灰 — 标题/正文 */ - --morandi-text-sub: #999999; /* 浅暖灰 — 辅助信息 */ - --morandi-text-muted:#B0B0B0; /* 更浅灰 — 占位等 */ - --morandi-border: #E8EAED; /* 极淡灰边框 */ - --morandi-divider: #EEEEEE; /* 分割线 */ - - /* ═══ 模块色(莫兰迪变体) ═══ */ - --module-discover: #A0B9D8; - --module-discover-light: #DCE6F0; - --module-knowledge: #B8D0C9; - --module-knowledge-light:#E4EFEB; - --module-life: #DAB8B0; - --module-life-light: #F2E4DF; - --module-profile: #C5CAD0; - --module-profile-light:#EDEEF0; - - /* ═══ 旧 CSS 变量兼容 ═══ */ - --primary: #A0B9D8; - --primary-dark: #8AA3C2; - --primary-light: #DCE6F0; - --shuren: #A0B9D8; - --shuren-light: #DCE6F0; - --shuren-gradient: linear-gradient(135deg, #A0B9D8, #8AA3C2); - --shushuo: #B8D0C9; - --shushuo-light: #E4EFEB; - --shushuo-gradient: linear-gradient(135deg, #B8D0C9, #A0BBAF); - --shusi: #DAB8B0; - --shusi-light: #F2E4DF; - --shusi-gradient: linear-gradient(135deg, #DAB8B0, #C9A39A); - --mine: #C5CAD0; - --mine-light: #EDEEF0; - --mine-gradient: linear-gradient(135deg, #C5CAD0, #AEB3B9); - --bg: #F0F2F5; - --card: #FAFBFC; - --text: #4A4A4A; - --text-secondary: #999999; - --text-muted: #B0B0B0; - --border: #E8EAED; - --sidebar-bg: #4A4A4A; - --sidebar-text: #999999; - --sidebar-active: #FAFBFC; - --radius-sm: 8px; - --radius: 12px; - --radius-lg: 16px; - --radius-xl: 20px; - --shadow-sm: 0 1px 3px rgba(0,0,0,0.04); - --shadow: 0 2px 8px rgba(0,0,0,0.05); - --shadow-lg: 0 4px 16px rgba(0,0,0,0.06); - --accent: var(--primary); - --accent-light: var(--primary-light); - --accent-gradient: linear-gradient(135deg, var(--primary), var(--primary-dark)); -} - -/* ═══════════════════════════════════════════════════════════ - Reset & Base - ═══════════════════════════════════════════════════════════ */ -* { margin: 0; padding: 0; box-sizing: border-box; } -html { scroll-behavior: smooth; -webkit-font-smoothing: antialiased; } -body { - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Microsoft YaHei", "Helvetica Neue", sans-serif; - background: #E2E5EA; - display: flex; - justify-content: center; - align-items: flex-start; - min-height: 100vh; - color: var(--morandi-text); - -webkit-tap-highlight-color: transparent; -} - -/* 手机外壳模拟 */ -body::before { - content: ""; - position: fixed; - top: 0; left: 50%; - transform: translateX(-50%); - width: min(390px, 100vw); - height: 100dvh; - background: var(--morandi-bg); - z-index: -1; - box-shadow: 0 0 80px rgba(0,0,0,0.06); -} - -/* ═══════════════════════════════════════════════════════════ - App Container - ═══════════════════════════════════════════════════════════ */ -.app-container { - display: flex; - flex-direction: column; - width: min(390px, 100vw); - max-width: 390px; - height: 100dvh; - overflow: hidden; - background: var(--morandi-bg); - position: relative; -} - -/* ═══════════════════════════════════════════════════════════ - Top Bar - ═══════════════════════════════════════════════════════════ */ -.top-bar { - display: flex; - align-items: center; - justify-content: space-between; - padding: calc(10px + env(safe-area-inset-top, 0px)) 16px 8px; - background: var(--morandi-bg); - z-index: 50; -} -.top-bar-brand { - display: flex; - align-items: center; - gap: 8px; -} -.brand-icon-sm { - width: 28px; height: 28px; - background: var(--morandi-blue); - border-radius: 50% 50% 50% 50% / 60% 60% 40% 40%; - display: flex; - align-items: center; - justify-content: center; - font-size: 16px; - color: #fff; - font-weight: 700; - box-shadow: 0 2px 6px rgba(160,185,216,0.25), inset 0 -3px 0 rgba(0,0,0,0.08); -} -.brand-text-sm { - font-size: 15px; - font-weight: 700; - color: var(--morandi-text); - letter-spacing: 0.3px; -} -.top-bar-actions { - display: flex; - align-items: center; - gap: 8px; -} -.icon-btn { - width: 36px; height: 36px; - border-radius: 50%; - border: 1px solid var(--morandi-border); - background: rgba(250, 251, 252, 0.85); - display: flex; - align-items: center; - justify-content: center; - font-size: 16px; - color: var(--morandi-text-sub); - cursor: pointer; - box-shadow: var(--shadow-sm); - transition: all 0.2s; -} -.icon-btn:active { - background: var(--morandi-blue-light); - border-color: var(--morandi-blue); - color: var(--morandi-blue); -} - -/* ═══════════════════════════════════════════════════════════ - Content Area - ═══════════════════════════════════════════════════════════ */ -.content-area { - flex: 1; - overflow-y: auto; - overflow-x: hidden; - background: var(--morandi-bg); - display: flex; - flex-direction: column; -} - -/* Section Title Bar */ -.section-title-bar { - padding: 12px 16px 10px; - background: var(--morandi-bg); - position: sticky; - top: 0; - z-index: 20; -} -.section-title { - font-size: 16px; - font-weight: 700; - color: var(--morandi-text); - letter-spacing: 0.2px; -} - -/* Module Banner */ -.module-banner { - display: flex; - align-items: center; - gap: 10px; - padding: 12px 14px; - margin: 0 12px 10px; - border-radius: var(--radius); - transition: all 0.3s; -} -.module-banner.shuren { background: var(--morandi-blue-light); } -.module-banner.shushuo { background: var(--morandi-green-light); } -.module-banner.shusi { background: var(--morandi-pink-light); } -.module-banner.shuwu { background: var(--module-profile-light); } -.banner-icon { font-size: 24px; flex-shrink: 0; opacity: 0.7; } -.banner-text { flex: 1; } -.banner-title { - font-size: 12px; - font-weight: 600; - margin-bottom: 2px; -} -.module-banner.shuren .banner-title { color: #6B8AAB; } -.module-banner.shushuo .banner-title { color: #7BA89A; } -.module-banner.shusi .banner-title { color: #B8958A; } -.module-banner.shuwu .banner-title { color: #8A8F94; } -.banner-sub { font-size: 11px; color: var(--morandi-text-sub); line-height: 1.5; } - -/* ═══════════════════════════════════════════════════════════ - Category Filter - ═══════════════════════════════════════════════════════════ */ -.category-filter { - display: flex; - flex-wrap: wrap; - gap: 6px; - padding: 0 12px 10px; -} -.cat-btn { - padding: 5px 12px; - border-radius: 14px; - border: 1px solid var(--morandi-border); - background: var(--morandi-card); - font-size: 11px; - font-weight: 500; - color: var(--morandi-text-sub); - cursor: pointer; - transition: all 0.2s; - white-space: nowrap; - box-shadow: var(--shadow-sm); -} -.cat-btn:active, -.cat-btn.active { - border-color: var(--morandi-blue); - background: var(--morandi-blue-light); - color: var(--morandi-blue); - font-weight: 600; -} - -/* ═══════════════════════════════════════════════════════════ - 存档面板 (Save Panel) — v8.3 - ═══════════════════════════════════════════════════════════ */ -.save-panel-overlay { - position: fixed; - top: 0; left: 0; right: 0; bottom: 0; - background: rgba(74, 74, 74, 0.45); - z-index: 180; - display: none; - align-items: center; - justify-content: center; - backdrop-filter: blur(4px); -} -.save-panel-overlay.show { display: flex; } -.save-panel { - background: var(--morandi-bg); - border-radius: var(--radius-xl); - width: min(360px, 92vw); - max-height: 78vh; - display: flex; - flex-direction: column; - overflow: hidden; - box-shadow: 0 16px 48px rgba(0,0,0,0.12); -} -.save-panel-header { - display: flex; - align-items: center; - gap: 10px; - padding: 14px 16px; - background: var(--morandi-card); - border-bottom: 1px solid var(--morandi-border); - flex-shrink: 0; -} -.sp-back { - border: none; - background: var(--morandi-bg); - padding: 5px 10px; - border-radius: 7px; - font-size: 12px; - color: var(--morandi-text-sub); - cursor: pointer; -} -.sp-title { - font-size: 15px; - font-weight: 700; - color: var(--morandi-text); - flex: 1; -} -.save-slots { - flex: 1; - overflow-y: auto; - padding: 12px 14px 20px; - display: flex; - flex-direction: column; - gap: 10px; -} -.save-slot { - background: var(--morandi-card); - border-radius: var(--radius); - padding: 14px 16px; - box-shadow: var(--shadow-sm); - border: 1px solid var(--morandi-border); - display: flex; - align-items: center; - gap: 12px; - cursor: pointer; - transition: transform 0.15s, border-color 0.2s; -} -.save-slot:active { transform: scale(0.98); } -.save-slot.active { - border-color: var(--morandi-blue); - box-shadow: 0 2px 12px rgba(160,185,216,0.15); -} -.save-slot-empty { - opacity: 0.5; - justify-content: center; - padding: 22px; - color: var(--morandi-text-muted); - font-size: 13px; -} -.save-slot-icon { - width: 42px; height: 42px; - border-radius: 10px; - display: flex; - align-items: center; - justify-content: center; - font-size: 20px; - flex-shrink: 0; -} -.save-slot-info { flex: 1; min-width: 0; } -.save-slot-name { - font-size: 13px; - font-weight: 700; - color: var(--morandi-text); - margin-bottom: 3px; -} -.save-slot-meta { - font-size: 10px; - color: var(--morandi-text-muted); - display: flex; - gap: 8px; -} -.save-slot-actions { - display: flex; - gap: 6px; - flex-shrink: 0; -} -.save-slot-btn { - padding: 5px 10px; - border: 1px solid var(--morandi-border); - border-radius: 7px; - background: var(--morandi-bg); - font-size: 10px; - font-weight: 600; - cursor: pointer; - color: var(--morandi-text-sub); - white-space: nowrap; - transition: all 0.15s; -} -.save-slot-btn.primary { - background: var(--morandi-blue); - color: #fff; - border-color: transparent; -} -.save-slot-btn:active { transform: scale(0.95); } - -/* ═══════════════════════════════════════════════════════════ - 结局画廊 (Ending Gallery) — v8.3 - ═══════════════════════════════════════════════════════════ */ -.gallery-overlay { - position: fixed; - top: 0; left: 0; right: 0; bottom: 0; - background: rgba(74, 74, 74, 0.45); - z-index: 180; - display: none; - align-items: center; - justify-content: center; - backdrop-filter: blur(4px); -} -.gallery-overlay.show { display: flex; } -.gallery-panel { - background: var(--morandi-bg); - border-radius: var(--radius-xl); - width: min(360px, 92vw); - max-height: 80vh; - display: flex; - flex-direction: column; - overflow: hidden; - box-shadow: 0 16px 48px rgba(0,0,0,0.12); -} -.gallery-header { - display: flex; - align-items: center; - gap: 10px; - padding: 14px 16px; - background: var(--morandi-card); - border-bottom: 1px solid var(--morandi-border); - flex-shrink: 0; -} -.gallery-progress { - font-size: 12px; - font-weight: 600; - color: var(--morandi-blue); - white-space: nowrap; -} -.gallery-grid { - flex: 1; - overflow-y: auto; - padding: 16px 14px 24px; - display: grid; - grid-template-columns: 1fr 1fr; - gap: 10px; -} -.gallery-card { - background: var(--morandi-card); - border-radius: var(--radius); - padding: 18px 14px; - text-align: center; - box-shadow: var(--shadow-sm); - display: flex; - flex-direction: column; - align-items: center; - gap: 8px; - transition: transform 0.15s; -} -.gallery-card.unlocked { cursor: pointer; } -.gallery-card.unlocked:active { transform: scale(0.96); } -.gallery-card.locked { - opacity: 0.4; - filter: grayscale(1); -} -.gallery-badge { - font-size: 32px; -} -.gallery-card-name { - font-size: 12px; - font-weight: 700; - color: var(--morandi-text); -} -.gallery-card-desc { - font-size: 10px; - color: var(--morandi-text-muted); - line-height: 1.4; -} -.gallery-card-lock { - font-size: 20px; - opacity: 0.5; -} - -/* ═══════════════════════════════════════════════════════════ - Search & Sub Nav - ═══════════════════════════════════════════════════════════ */ -.search-header { - padding: 10px 14px 4px; - background: var(--morandi-bg); - position: sticky; - top: 0; - z-index: 25; -} -.search-bar-wrap { - display: flex; - align-items: center; - background: var(--morandi-card); - padding: 9px 14px; - border-radius: 22px; - box-shadow: 0 1px 4px rgba(0,0,0,0.04); - border: 1px solid var(--morandi-border); - transition: border-color 0.2s, box-shadow 0.2s; -} -.search-bar-wrap:focus-within { - border-color: var(--morandi-blue); - box-shadow: 0 1px 8px rgba(160,185,216,0.15); -} -.search-icon { margin-right: 8px; font-size: 13px; opacity: 0.35; } -.search-bar-wrap input { - flex: 1; - border: none; - background: transparent; - outline: none; - font-size: 13px; - color: var(--morandi-text); -} -.search-bar-wrap input::placeholder { color: var(--morandi-text-muted); } - -.sub-nav { - display: flex; - gap: 22px; - padding: 6px 16px 8px; - background: var(--morandi-bg); - border-bottom: 1px solid var(--morandi-border); - margin-bottom: 10px; -} -.sub-nav-btn { - border: none; - background: transparent; - font-size: 13px; - font-weight: 500; - color: var(--morandi-text-sub); - cursor: pointer; - padding: 4px 0; - position: relative; - transition: color 0.2s; - letter-spacing: 0.2px; -} -.sub-nav-btn:active { color: var(--morandi-text); } -.sub-nav-btn.active { - color: var(--morandi-text); - font-weight: 700; -} -.sub-nav-btn.active::after { - content: ""; - position: absolute; - bottom: 0; left: 50%; - transform: translateX(-50%); - width: 18px; - height: 3px; - background: var(--morandi-blue); - border-radius: 2px; -} - -/* ═══════════════════════════════════════════════════════════ - Discipline Grid - ═══════════════════════════════════════════════════════════ */ -.discipline-grid-container { - padding: 14px 14px 20px; - animation: fadeIn 0.3s ease; -} -.grid-section-title { - font-size: 15px; - font-weight: 700; - color: var(--morandi-text); - margin-bottom: 16px; - letter-spacing: 0.2px; -} -.discipline-grid { - display: grid; - grid-template-columns: repeat(3, 1fr); - gap: 12px 10px; -} -.discipline-item { - display: flex; - flex-direction: column; - align-items: center; - gap: 8px; - cursor: pointer; - transition: transform 0.2s; - padding: 16px 6px 12px; - border-radius: var(--radius-lg); - background: var(--morandi-card); - box-shadow: var(--shadow-sm); -} -.discipline-item:active { - transform: scale(0.95); - box-shadow: var(--shadow); -} -.discipline-icon-box { - width: 56px; - height: 56px; - border-radius: 16px; - display: flex; - align-items: center; - justify-content: center; - font-size: 24px; - font-weight: 700; - transition: all 0.2s; -} -.discipline-item:hover .discipline-icon-box { - transform: translateY(-2px); - box-shadow: var(--shadow); -} -.discipline-label { - font-size: 11px; - font-weight: 600; - color: var(--morandi-text); - text-align: center; - white-space: nowrap; - letter-spacing: 0.2px; -} -.discipline-active-header { - padding: 10px 14px; - background: var(--morandi-bg); - border-bottom: 1px solid var(--morandi-border); - margin-bottom: 10px; - display: flex; - align-items: center; -} -.back-to-grid { - display: flex; - align-items: center; - gap: 8px; - border: none; - background: transparent; - cursor: pointer; - color: var(--morandi-text); - font-size: 14px; - font-weight: 700; - padding: 4px 0; -} -.back-icon { - font-size: 16px; - color: var(--morandi-blue); -} - -/* ═══════════════════════════════════════════════════════════ - Video Grid (双列卡片) - ═══════════════════════════════════════════════════════════ */ -.video-list { - display: grid; - grid-template-columns: 1fr 1fr; - gap: 10px; - padding: 0 12px 20px; -} -.video-card { - background: var(--morandi-card); - border-radius: var(--radius); - overflow: hidden; - box-shadow: var(--shadow-sm); - cursor: pointer; - transition: all 0.2s; - display: flex; - flex-direction: column; -} -.video-card:active { - transform: scale(0.98); - box-shadow: var(--shadow); -} - -/* 封面区域 — 莫兰迪杂色底 */ -.vc-cover { - width: 100%; - aspect-ratio: 16/10; - position: relative; - display: flex; - align-items: center; - justify-content: center; - overflow: hidden; -} -.vc-cover-placeholder { - font-size: 28px; - opacity: 0.18; -} -/* 时长徽章 */ -.vc-duration { - position: absolute; - bottom: 6px; right: 6px; - background: rgba(74, 74, 74, 0.55); - backdrop-filter: blur(4px); - color: #fff; - font-size: 10px; - font-weight: 500; - padding: 2px 6px; - border-radius: 5px; - letter-spacing: 0.3px; -} - -/* 内容区 */ -.vc-content { - padding: 8px 10px 10px; - flex: 1; - display: flex; - flex-direction: column; -} -.vc-title { - font-size: 12px; - font-weight: 600; - line-height: 1.45; - color: var(--morandi-text); - margin-bottom: 5px; - display: -webkit-box; - -webkit-line-clamp: 2; - -webkit-box-orient: vertical; - overflow: hidden; - min-height: 34px; - letter-spacing: 0.1px; -} -.vc-info-row { - display: flex; - align-items: center; - gap: 5px; - margin-top: auto; -} -.vc-author-avatar { - width: 16px; height: 16px; - border-radius: 50%; - display: flex; - align-items: center; - justify-content: center; - font-size: 8px; - color: #fff; - flex-shrink: 0; -} -.vc-author-name { - font-size: 10px; - color: var(--morandi-text-sub); - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - flex: 1; -} -.vc-stat { - font-size: 10px; - color: var(--morandi-text-muted); - display: flex; - align-items: center; - gap: 2px; -} - -/* 收藏按钮 */ -.vc-fav { - position: absolute; - top: 6px; right: 6px; - width: 26px; height: 26px; - border-radius: 50%; - border: none; - background: rgba(74,74,74,0.28); - backdrop-filter: blur(3px); - color: #fff; - font-size: 13px; - cursor: pointer; - display: flex; - align-items: center; - justify-content: center; - opacity: 0; - transition: opacity 0.2s, transform 0.15s; - z-index: 2; -} -.video-card:hover .vc-fav, -.video-card:active .vc-fav { opacity: 1; } -.vc-fav.active { - opacity: 1; - background: rgba(160, 185, 216, 0.75); -} -.vc-fav:active { transform: scale(1.15); } - -/* ═══════════════════════════════════════════════════════════ - Empty & Loading States - ═══════════════════════════════════════════════════════════ */ -.loading { - display: flex; - flex-direction: column; - align-items: center; - padding: 60px 0; - color: var(--morandi-text-muted); -} -.spinner { - width: 28px; height: 28px; - border: 2.5px solid var(--morandi-border); - border-top-color: var(--morandi-blue); - border-radius: 50%; - animation: spin 0.7s linear infinite; - margin-bottom: 10px; -} -@keyframes spin { to { transform: rotate(360deg); } } -.empty-state { - text-align: center; - padding: 60px 20px; - color: var(--morandi-text-muted); -} -.empty-state .icon { font-size: 40px; margin-bottom: 10px; opacity: 0.4; } -.empty-state h3 { font-size: 14px; color: var(--morandi-text-sub); margin-bottom: 4px; font-weight: 600; } -.empty-state p { font-size: 12px; } - -/* ═══════════════════════════════════════════════════════════ - Knowledge Feed (数说 · 知识卡片) - ═══════════════════════════════════════════════════════════ */ - -/* Hero 大横幅 */ -.shushuo-hero { - padding: 0 12px 14px; - animation: fadeIn 0.3s ease; -} -.hero-card { - background: linear-gradient(145deg, #C0CAD4 0%, #A8B8C8 60%, #B5C3D0 100%); - border-radius: var(--radius-lg); - padding: 22px 18px; - color: #fff; - position: relative; - overflow: hidden; - box-shadow: 0 4px 16px rgba(192, 202, 212, 0.2); -} -.hero-card::before { - content: ""; - position: absolute; - top: -20px; right: -20px; - width: 90px; height: 90px; - border-radius: 50%; - background: rgba(255,255,255,0.05); -} -.hero-card::after { - content: ""; - position: absolute; - bottom: -25px; left: -10px; - width: 70px; height: 70px; - border-radius: 50%; - background: rgba(255,255,255,0.03); -} -.hero-badge { - display: inline-block; - padding: 3px 10px; - background: rgba(255,255,255,0.15); - border-radius: 10px; - font-size: 10px; - font-weight: 600; - margin-bottom: 12px; - letter-spacing: 0.5px; -} -.hero-title { - font-size: 19px; - font-weight: 800; - line-height: 1.35; - margin-bottom: 6px; - position: relative; - z-index: 1; -} -.hero-desc { - font-size: 11px; - opacity: 0.72; - line-height: 1.5; - margin-bottom: 12px; - position: relative; - z-index: 1; -} -.hero-stats { - display: flex; - gap: 16px; - font-size: 10px; - opacity: 0.55; - position: relative; - z-index: 1; -} - -/* 横向话题标签 */ -.shushuo-topics { - padding: 0 12px 8px; - overflow: hidden; -} -.topics-scroll { - display: flex; - gap: 7px; - overflow-x: auto; - scrollbar-width: none; - padding-bottom: 4px; -} -.topics-scroll::-webkit-scrollbar { display: none; } -.topic-pill { - display: inline-flex; - padding: 5px 12px; - border-radius: 14px; - border: 1px solid var(--morandi-border); - background: var(--morandi-card); - font-size: 11px; - font-weight: 500; - color: var(--morandi-text-sub); - cursor: pointer; - white-space: nowrap; - flex-shrink: 0; - transition: all 0.2s; -} -.topic-pill:active, -.topic-pill.active { - border-color: var(--morandi-green); - background: var(--morandi-green-light); - color: #7BA89A; - font-weight: 600; -} - -/* 知识卡片 Feed */ -.shushuo-feed { - display: flex; - flex-direction: column; - gap: 12px; - padding: 0 12px 20px; - animation: fadeIn 0.4s ease; -} -.feed-card { - background: var(--morandi-card); - border-radius: var(--radius-lg); - overflow: hidden; - box-shadow: var(--shadow-sm); - cursor: pointer; - transition: transform 0.2s; -} -.feed-card:active { transform: scale(0.98); } -.feed-card-visual { - width: 100%; - height: 132px; - position: relative; - display: flex; - align-items: center; - justify-content: center; - overflow: hidden; -} -.feed-card-icon { - font-size: 42px; - opacity: 0.22; - z-index: 1; -} -.feed-card-question { - position: absolute; - bottom: 10px; left: 14px; right: 14px; - font-size: 14px; - font-weight: 700; - color: #fff; - z-index: 1; - text-shadow: 0 1px 2px rgba(0,0,0,0.15); - line-height: 1.4; -} -.feed-card-body { - padding: 10px 14px 14px; -} -.feed-card-title { - font-size: 15px; - font-weight: 700; - line-height: 1.4; - margin-bottom: 5px; - color: var(--morandi-text); -} -.feed-card-desc { - font-size: 12px; - color: var(--morandi-text-sub); - line-height: 1.6; - margin-bottom: 8px; -} -.feed-card-footer { - display: flex; - align-items: center; - gap: 8px; -} -.feed-card-topic { - font-size: 10px; - font-weight: 500; -} -.feed-card-time { - font-size: 10px; - color: var(--morandi-text-muted); -} -.feed-card-fav { - position: absolute; - top: 6px; right: 6px; - width: 26px; height: 26px; - border-radius: 50%; - border: none; - background: rgba(74,74,74,0.2); - color: #fff; - font-size: 13px; - cursor: pointer; - display: flex; - align-items: center; - justify-content: center; - transition: transform 0.15s; - z-index: 2; -} -.feed-card-fav.active { background: rgba(160, 185, 216, 0.7); } -.feed-card-fav:active { transform: scale(1.15); } - -/* ═══════════════════════════════════════════════════════════ - 个人中心 (Profile) — 紧凑布局,完整显示 - ═══════════════════════════════════════════════════════════ */ -.profile-page { - padding: 6px 12px 20px; - animation: fadeIn 0.3s ease; - display: flex; - flex-direction: column; - gap: 6px; -} - -/* 用户信息面板 */ -.profile-card { - background: linear-gradient(135deg, #C5CAD0 0%, #B5C0CA 40%, #C0C8D0 100%); - border-radius: var(--radius-lg); - padding: 14px 14px 12px; - color: #fff; - box-shadow: 0 6px 20px rgba(197, 202, 208, 0.2); - flex-shrink: 0; - cursor: pointer; - transition: opacity 0.15s; -} -.profile-card:active { opacity: 0.92; } -.profile-main { - display: flex; - align-items: center; - gap: 10px; -} -.profile-avatar { - width: 48px; height: 48px; - border-radius: 50% 50% 50% 50% / 60% 60% 40% 40%; - box-shadow: inset 0 -3px 0 rgba(0,0,0,0.08); - display: flex; - align-items: center; - justify-content: center; - background: rgba(255,255,255,0.2); - font-size: 20px; - font-weight: 800; - flex-shrink: 0; -} -.profile-meta { flex: 1; min-width: 0; } -.profile-name { - font-size: 16px; - font-weight: 800; - margin-bottom: 1px; - letter-spacing: 0.2px; -} -.profile-id { - font-size: 10px; - opacity: 0.7; - margin-bottom: 6px; -} -.profile-tags { - display: flex; - flex-wrap: wrap; - gap: 5px; -} -.profile-tags span { - padding: 2px 7px; - border-radius: 999px; - background: rgba(255,255,255,0.16); - font-size: 10px; - font-weight: 500; -} -.profile-edit { - margin-top: 8px; - width: fit-content; - padding: 4px 10px; - border-radius: 999px; - background: rgba(255,255,255,0.14); - font-size: 10px; - font-weight: 600; - cursor: pointer; -} - -/* 统计行 */ -.profile-stats { - display: grid; - grid-template-columns: repeat(3, 1fr); - gap: 6px; - flex-shrink: 0; -} -.profile-stat { - background: var(--morandi-card); - border-radius: var(--radius); - padding: 10px 6px; - text-align: center; - box-shadow: var(--shadow-sm); - border: 1px solid var(--morandi-border); - cursor: pointer; - transition: transform 0.15s; -} -.profile-stat:active { transform: scale(0.96); } -.profile-stat-num { - font-size: 17px; - font-weight: 800; - color: var(--morandi-blue); - margin-bottom: 1px; -} -.profile-stat-label { - font-size: 10px; - color: var(--morandi-text-sub); -} - -/* 功能列表(紧凑型) */ -.profile-section { - flex-shrink: 0; -} -.profile-section-title { - font-size: 10px; - font-weight: 700; - color: var(--morandi-text-sub); - margin-bottom: 4px; - padding: 0 2px; - letter-spacing: 0.3px; - text-transform: uppercase; -} -.profile-menu-card { - background: var(--morandi-card); - border-radius: var(--radius); - box-shadow: var(--shadow-sm); - overflow: hidden; -} -.profile-menu-item { - display: flex; - align-items: center; - justify-content: space-between; - padding: 9px 12px; - border-bottom: 1px solid var(--morandi-divider); - cursor: pointer; - transition: background 0.15s; -} -.profile-menu-item:last-child { border-bottom: none; } -.profile-menu-item:active { background: var(--morandi-bg); } -.profile-menu-left { - display: flex; - align-items: center; - gap: 8px; - font-size: 12px; - font-weight: 600; - color: var(--morandi-text); -} -.menu-icon { - width: 22px; height: 22px; - border-radius: 6px; - background: var(--morandi-bg); - display: inline-flex; - align-items: center; - justify-content: center; - color: var(--morandi-blue); - font-size: 11px; - font-weight: 700; -} -.menu-arrow { - font-size: 14px; - color: var(--morandi-text-muted); - font-weight: 400; -} - -/* ═══════════════════════════════════════════════════════════ - Bottom TabBar - ═══════════════════════════════════════════════════════════ */ -.bottom-tab-bar { - display: flex; - align-items: center; - justify-content: space-around; - gap: 2px; - background: rgba(255, 255, 255, 0.96); - backdrop-filter: blur(20px); - -webkit-backdrop-filter: blur(20px); - padding: 8px 10px calc(10px + env(safe-area-inset-bottom, 0px)); - border-top: 1px solid var(--morandi-border); - z-index: 60; - box-shadow: 0 -4px 20px rgba(160, 185, 216, 0.06); -} -.tab-btn { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - gap: 3px; - border: none; - background: transparent; - color: var(--morandi-text-muted); - cursor: pointer; - width: 78px; - min-height: 48px; - border-radius: 14px; - transition: all 0.2s ease; -} -.tab-icon { - width: 20px; height: 20px; - position: relative; - transition: transform 0.2s, opacity 0.2s; - opacity: 0.6; -} -.tab-label { - font-size: 10px; - font-weight: 500; - letter-spacing: 0.2px; -} -.tab-btn.active { - color: var(--morandi-blue); - background: var(--morandi-blue-light); -} -.tab-btn.active .tab-icon { - transform: translateY(-1px); - opacity: 1; -} -.tab-btn.active .tab-label { - font-weight: 700; -} - -/* Tab 图标 — 简约线性风格 */ -.tab-icon::before, -.tab-icon::after { - content: ""; - position: absolute; - box-sizing: border-box; -} -.tab-icon-discover::before { - inset: 3px; - border: 1.8px solid currentColor; - border-radius: 5px; -} -.tab-icon-discover::after { - width: 4px; height: 8px; - right: 5px; top: 7px; - background: currentColor; - border-radius: 2px; -} -.tab-icon-knowledge::before { - left: 4px; right: 4px; bottom: 3px; - height: 10px; - border: 1.8px solid currentColor; - border-top: none; - border-radius: 1px 1px 4px 4px; -} -.tab-icon-knowledge::after { - left: 6px; top: 2px; - width: 10px; height: 10px; - background: currentColor; - clip-path: polygon(50% 0, 100% 40%, 100% 100%, 0 100%, 0 40%); -} -.tab-icon-life::before { - left: 5px; right: 5px; top: 3px; bottom: 3px; - border: 1.8px solid currentColor; - border-radius: 3px 3px 7px 7px; -} -.tab-icon-life::after { - left: 8px; right: 8px; top: 8px; - height: 2px; - background: currentColor; - border-radius: 2px; -} -.tab-icon-mine::before { - width: 7px; height: 7px; - left: 3px; top: 3px; - background: currentColor; - border-radius: 2px; - box-shadow: 9px 0 0 currentColor, 0 9px 0 currentColor, 9px 9px 0 currentColor; -} - -/* ═══════════════════════════════════════════════════════════ - AI Chat - ═══════════════════════════════════════════════════════════ */ -.chat-overlay { - display: none; - position: fixed; - inset: 0; - z-index: 200; - background: rgba(74, 74, 74, 0.35); - justify-content: center; - align-items: center; - backdrop-filter: blur(4px); -} -.chat-overlay.show { display: flex; } -.chat-panel { - width: 380px; - max-width: 93vw; - height: 78vh; - background: var(--morandi-card); - border-radius: var(--radius-xl); - display: flex; - flex-direction: column; - animation: modalIn 0.25s cubic-bezier(0.16, 1, 0.3, 1); - box-shadow: 0 20px 50px rgba(0,0,0,0.12); - overflow: hidden; -} -@keyframes modalIn { - from { opacity: 0; transform: scale(0.95) translateY(10px); } - to { opacity: 1; transform: scale(1) translateY(0); } -} -.chat-header { - display: flex; - align-items: center; - justify-content: space-between; - padding: 14px 18px; - border-bottom: 1px solid var(--morandi-border); - font-weight: 700; - font-size: 14px; - color: var(--morandi-text); -} -.chat-header-left { display: flex; align-items: center; gap: 10px; } -.ai-avatar { - width: 30px; height: 30px; - background: var(--morandi-blue); - border-radius: 9px; - display: flex; - align-items: center; - justify-content: center; - font-size: 15px; - color: #fff; - box-shadow: 0 2px 6px rgba(160,185,216,0.3); -} -.chat-close { - width: 28px; height: 28px; - border-radius: 50%; - border: none; - background: var(--morandi-bg); - cursor: pointer; - font-size: 14px; - display: flex; - align-items: center; - justify-content: center; - color: var(--morandi-text-sub); -} -.chat-messages { - flex: 1; - overflow-y: auto; - padding: 14px; - display: flex; - flex-direction: column; - gap: 10px; - background: var(--morandi-bg); -} -.chat-msg { display: flex; animation: msgIn 0.3s ease; } -@keyframes msgIn { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: translateY(0); } } -.chat-msg.assistant { justify-content: flex-start; } -.chat-msg.user { justify-content: flex-end; } -.msg-bubble { - max-width: 82%; - padding: 9px 13px; - border-radius: 13px; - font-size: 13px; - line-height: 1.6; - word-break: break-word; -} -.chat-msg.assistant .msg-bubble { - background: #fff; - border-bottom-left-radius: 3px; - box-shadow: var(--shadow-sm); -} -.chat-msg.user .msg-bubble { - background: linear-gradient(135deg, #A0B9D8, #8AA3C2); - color: #fff; - border-bottom-right-radius: 3px; -} -.quick-asks { - display: flex; - flex-wrap: wrap; - gap: 5px; - padding: 6px 14px; -} -.quick-ask { - padding: 5px 11px; - border-radius: 12px; - border: 1px solid var(--morandi-border); - background: #fff; - font-size: 11px; - color: var(--morandi-text-sub); - cursor: pointer; - transition: all 0.15s; -} -.quick-ask:active { - background: var(--morandi-blue-light); - border-color: var(--morandi-blue); - color: var(--morandi-blue); -} -.chat-input-area { - display: flex; - padding: 8px 14px 12px; - border-top: 1px solid var(--morandi-border); - gap: 8px; - background: #fff; -} -.chat-input-area input { - flex: 1; - padding: 9px 13px; - border: 1px solid var(--morandi-border); - border-radius: 18px; - font-size: 13px; - outline: none; - background: var(--morandi-bg); - transition: border-color 0.2s; -} -.chat-input-area input:focus { border-color: var(--morandi-blue); } -.send-btn { - min-width: 36px; height: 36px; - border: none; - border-radius: 50%; - background: var(--morandi-blue); - color: #fff; - font-size: 15px; - cursor: pointer; - flex-shrink: 0; - display: flex; - align-items: center; - justify-content: center; - box-shadow: 0 2px 8px rgba(160,185,216,0.25); -} -.send-btn:active { transform: scale(0.93); } -.typing-indicator { display: flex; gap: 4px; padding: 3px 0; } -.typing-indicator span { - width: 5px; height: 5px; - border-radius: 50%; - background: var(--morandi-text-muted); - animation: bounce 1.2s infinite; -} -.typing-indicator span:nth-child(2) { animation-delay: 0.2s; } -.typing-indicator span:nth-child(3) { animation-delay: 0.4s; } -@keyframes bounce { - 0%, 60%, 100% { transform: translateY(0); } - 30% { transform: translateY(-6px); } -} - -/* ═══════════════════════════════════════════════════════════ - Video Modal - ═══════════════════════════════════════════════════════════ */ -.video-modal { - display: none; - position: fixed; - inset: 0; - z-index: 200; - background: rgba(74, 74, 74, 0.5); - justify-content: center; - align-items: center; - backdrop-filter: blur(4px); -} -.video-modal.show { display: flex; } -.video-modal-content { - width: 360px; - max-width: 93vw; - max-height: 86vh; - background: var(--morandi-card); - border-radius: var(--radius-lg); - overflow-y: auto; - animation: modalIn 0.25s cubic-bezier(0.16, 1, 0.3, 1); -} -.video-modal-header { - display: flex; - justify-content: space-between; - align-items: center; - padding: 14px 16px; - border-bottom: 1px solid var(--morandi-border); -} -.video-modal-header h3 { font-size: 14px; font-weight: 700; flex: 1; margin-right: 10px; line-height: 1.4; color: var(--morandi-text); } -.video-modal-header button { - width: 28px; height: 28px; border-radius: 50%; border: none; - background: var(--morandi-bg); cursor: pointer; font-size: 14px; - display: flex; align-items: center; justify-content: center; flex-shrink: 0; color: var(--morandi-text-sub); -} -.video-player-area { - background: #E8EBEF; - aspect-ratio: 16/9; - display: flex; - align-items: center; - justify-content: center; -} -.video-placeholder { text-align: center; color: rgba(74,74,74,0.35); } -.play-icon { - width: 46px; height: 46px; border-radius: 50%; - background: rgba(160,185,216,0.25); - display: flex; - align-items: center; justify-content: center; font-size: 20px; - margin: 0 auto 8px; -} -.video-placeholder p { font-size: 11px; } -.video-info { padding: 12px 16px; } -.video-desc { font-size: 12px; line-height: 1.6; color: var(--morandi-text-sub); margin-bottom: 8px; } -.video-meta { font-size: 10px; color: var(--morandi-text-muted); display: flex; flex-wrap: wrap; gap: 10px; } -.video-actions { padding: 0 16px 16px; display: flex; gap: 8px; } -.btn-primary, .btn-secondary { - flex: 1; padding: 9px; border-radius: 10px; border: none; - font-size: 12px; font-weight: 600; cursor: pointer; text-align: center; - transition: transform 0.15s; -} -.btn-primary:active, .btn-secondary:active { transform: scale(0.97); } -.btn-primary { background: var(--morandi-blue); color: #fff; box-shadow: 0 2px 8px rgba(160,185,216,0.2); } -.btn-secondary { background: var(--morandi-bg); color: var(--morandi-text); } - -/* ═══════════════════════════════════════════════════════════ - Visual Novel Engine - ═══════════════════════════════════════════════════════════ */ -.vn-header { - display: flex; - flex-direction: column; - gap: 8px; - margin-bottom: 10px; - padding: 0 12px; -} -.vn-chapter-badge { - align-self: flex-start; - padding: 4px 12px; - border-radius: 999px; - font-size: 11px; - font-weight: 700; - color: #fff; - background: var(--morandi-green); - box-shadow: 0 2px 6px rgba(184, 208, 201, 0.3); - letter-spacing: 0.3px; -} -.vn-stats { - display: flex; - gap: 6px; - flex-wrap: wrap; -} -.stat-badge { - font-size: 10px; - padding: 4px 8px; - border-radius: 10px; - color: #fff; - font-weight: 600; - box-shadow: 0 1px 3px rgba(0,0,0,0.06); -} -.vn-actions { - display: flex; - gap: 6px; - padding: 0 12px; -} -.vn-btn { - background: var(--morandi-card); - border: 1px solid var(--morandi-border); - padding: 5px 10px; - border-radius: 8px; - font-size: 11px; - cursor: pointer; - box-shadow: var(--shadow-sm); - color: var(--morandi-text-sub); - font-weight: 600; - transition: all 0.2s; -} -.vn-btn:active { - background: var(--morandi-green-light); - border-color: var(--morandi-green); - color: #7BA89A; -} -.story-scene { - background: var(--morandi-card); - margin: 0 12px 12px; - border-radius: var(--radius-lg); - padding: 18px; - min-height: 260px; - box-shadow: var(--shadow-sm); - position: relative; - overflow: hidden; - display: flex; - flex-direction: column; - justify-content: flex-end; -} -.vn-background { - position: absolute; - top: 0; left: 0; - width: 100%; - height: 100%; - object-fit: cover; - z-index: 0; - border-radius: var(--radius-lg); -} -.vn-sprite { - position: absolute; - bottom: 0; - right: 0; - max-height: 75%; - max-width: 55%; - object-fit: contain; - z-index: 1; - pointer-events: none; - transition: opacity 0.4s ease; -} -.story-text-box { - position: relative; - z-index: 2; - background: rgba(250, 251, 252, 0.94); - backdrop-filter: blur(4px); - padding: 12px; - border-radius: 9px; - border: 1px solid var(--morandi-border); -} -.story-character { - font-weight: 800; - color: var(--morandi-green); - margin-bottom: 5px; - font-size: 12px; - letter-spacing: 0.3px; -} -.story-text { - font-size: 13px; - line-height: 1.7; - color: var(--morandi-text); - min-height: 44px; - white-space: pre-line; -} -.story-choices { - display: flex; - flex-direction: column; - gap: 8px; - padding: 0 12px 20px; -} -.choice-btn { - background: var(--morandi-card); - border: 1px solid var(--morandi-border); - border-radius: var(--radius); - padding: 12px 14px; - font-size: 13px; - font-weight: 600; - color: var(--morandi-text); - text-align: left; - cursor: pointer; - transition: all 0.2s; - box-shadow: var(--shadow-sm); - display: flex; - flex-direction: column; - gap: 5px; - animation: fadeInUp 0.3s ease both; -} -.choice-btn:hover { - background: var(--morandi-green-light); - border-color: var(--morandi-green); -} -.choice-btn:active { - transform: scale(0.98); - background: var(--morandi-green-light); -} -.choice-hint { - font-size: 10px; - color: #7BA89A; - font-weight: 500; -} -.choice-text { line-height: 1.5; } -@keyframes fadeInUp { - from { opacity: 0; transform: translateY(8px); } - to { opacity: 1; transform: translateY(0); } -} -@keyframes fadeIn { - from { opacity: 0; transform: translateY(8px); } - to { opacity: 1; transform: translateY(0); } -} - -/* ═══════════════════════════════════════════════════════════ - Simulator - ═══════════════════════════════════════════════════════════ */ -.sim-header { - display: flex; - flex-direction: column; - gap: 8px; - margin: 10px 0; - padding: 0 12px; -} -.sim-year-badge { - align-self: flex-start; - padding: 4px 14px; - border-radius: 999px; - font-size: 11px; - font-weight: 700; - color: #fff; - box-shadow: 0 2px 6px rgba(0,0,0,0.1); -} -.sim-stats { - display: flex; - gap: 6px; - flex-wrap: wrap; -} -.sim-stat-bar { - font-size: 10px; - padding: 4px 8px; - border-radius: 10px; - color: #fff; - font-weight: 600; - box-shadow: 0 1px 3px rgba(0,0,0,0.06); -} -.sim-actions-top { - display: flex; - gap: 6px; - padding: 0 12px 8px; -} -.sim-scene { - background: var(--morandi-card); - margin: 0 12px 12px; - border-radius: var(--radius-lg); - padding: 18px; - min-height: 180px; - box-shadow: var(--shadow-sm); - position: relative; -} -.sim-character { - font-weight: 800; - color: var(--morandi-pink); - margin-bottom: 8px; - font-size: 13px; - letter-spacing: 0.3px; -} -.sim-text { - font-size: 13px; - line-height: 1.7; - color: var(--morandi-text); - min-height: 54px; - white-space: pre-line; -} -.sim-choices { - display: flex; - flex-direction: column; - gap: 8px; - padding: 0 12px 28px; -} -.sim-choice-btn { - background: var(--morandi-card); - border: 1px solid var(--morandi-border); - border-radius: var(--radius); - padding: 12px 14px; - font-size: 13px; - font-weight: 600; - color: var(--morandi-text); - text-align: left; - cursor: pointer; - transition: all 0.2s; - box-shadow: var(--shadow-sm); - display: flex; - flex-direction: column; - gap: 5px; - animation: fadeInUp 0.3s ease both; -} -.sim-choice-btn:hover { - background: var(--morandi-pink-light); - border-color: var(--morandi-pink); -} -.sim-choice-btn:active { transform: scale(0.98); } -.sim-hint { - font-size: 10px; - color: #B8958A; - font-weight: 500; -} -.sim-hint-tip { - font-size: 10px; - color: var(--morandi-text-muted); - font-weight: 400; -} -.sim-choice-text { line-height: 1.5; } - -/* ═══════════════════════════════════════════════════════════ - Ending Overlay - ═══════════════════════════════════════════════════════════ */ -.ending-overlay { - position: fixed; - top: 0; left: 0; right: 0; bottom: 0; - background: rgba(74, 74, 74, 0.5); - z-index: 200; - display: none; - align-items: center; - justify-content: center; -} -.ending-overlay.show { display: flex; } -.ending-card { - background: var(--morandi-card); - border-radius: var(--radius-xl); - padding: 24px 22px; - width: min(320px, 88vw); - text-align: center; - box-shadow: 0 16px 48px rgba(0,0,0,0.15); - animation: fadeInUp 0.4s ease; - display: flex; - flex-direction: column; - gap: 10px; -} -.ending-badge { font-size: 44px; } -.ending-type { - font-size: 12px; - color: var(--morandi-text-muted); - font-weight: 600; - letter-spacing: 1px; -} -.ending-title { - font-size: 16px; - font-weight: 700; - color: var(--morandi-text); -} -.ending-scene { - font-size: 12px; - color: var(--morandi-text-sub); - line-height: 1.7; - background: var(--morandi-bg); - border-radius: var(--radius); - padding: 10px; -} - -/* ═══════════════════════════════════════════════════════════ - Avatar Picker - ═══════════════════════════════════════════════════════════ */ -.avatar-picker-overlay { - position: fixed; - top: 0; left: 0; right: 0; bottom: 0; - background: rgba(74, 74, 74, 0.45); - z-index: 150; - display: none; - align-items: center; - justify-content: center; -} -.avatar-picker-overlay.show { display: flex; } -.avatar-picker-card { - background: var(--morandi-card); - border-radius: var(--radius-xl); - padding: 18px; - width: min(320px, 88vw); - box-shadow: 0 16px 48px rgba(0,0,0,0.15); - display: flex; - flex-direction: column; - gap: 12px; -} -.picker-title { - font-size: 14px; - font-weight: 700; - text-align: center; - color: var(--morandi-text); -} -.picker-emoji-grid { - display: flex; - flex-wrap: wrap; - gap: 5px; - justify-content: center; -} -.picker-emoji-btn { - width: 34px; height: 34px; - border: 1.5px solid var(--morandi-border); - border-radius: 8px; - background: var(--morandi-bg); - font-size: 16px; - cursor: pointer; - display: flex; - align-items: center; - justify-content: center; - transition: all 0.15s; -} -.picker-emoji-btn:active { transform: scale(0.9); } -.picker-color-row { - display: flex; - gap: 8px; - justify-content: center; -} -.picker-color-btn { - width: 30px; height: 30px; - border: none; - border-radius: 50%; - cursor: pointer; - transition: transform 0.15s; -} -.picker-color-btn:active { transform: scale(0.85); } -.picker-actions { - display: flex; - gap: 8px; -} -.picker-btn { - flex: 1; - padding: 9px; - border: 1px solid var(--morandi-border); - border-radius: 10px; - background: var(--morandi-bg); - font-size: 12px; - font-weight: 600; - cursor: pointer; - text-align: center; - color: var(--morandi-text-sub); -} -.picker-btn.primary { - background: var(--morandi-blue); - color: #fff; - border-color: transparent; - box-shadow: 0 2px 8px rgba(160,185,216,0.2); -} - -/* ═══════════════════════════════════════════════════════════ - Profile List Panel - ═══════════════════════════════════════════════════════════ */ -.profile-list-panel { - position: absolute; - top: 0; left: 0; right: 0; bottom: 0; - background: var(--morandi-bg); - z-index: 30; - display: flex; - flex-direction: column; - overflow: hidden; -} -.plp-header { - display: flex; - align-items: center; - gap: 10px; - padding: 12px 14px; - background: var(--morandi-card); - border-bottom: 1px solid var(--morandi-border); - position: sticky; - top: 0; - z-index: 5; -} -.plp-back { - border: none; - background: var(--morandi-bg); - padding: 5px 10px; - border-radius: 7px; - font-size: 12px; - color: var(--morandi-text-sub); - cursor: pointer; -} -.plp-title { - font-size: 14px; - font-weight: 700; - color: var(--morandi-text); -} -.plp-content { - flex: 1; - overflow-y: auto; - padding: 10px 12px; -} -.history-item { - display: flex; - align-items: center; - gap: 10px; - background: var(--morandi-card); - border-radius: var(--radius); - padding: 10px 12px; - margin-bottom: 7px; - box-shadow: var(--shadow-sm); -} -.hi-icon { - width: 40px; height: 40px; - border-radius: 8px; - display: flex; - align-items: center; - justify-content: center; - font-size: 18px; - flex-shrink: 0; -} -.hi-info { flex: 1; min-width: 0; } -.hi-title { - font-size: 13px; - font-weight: 600; - color: var(--morandi-text); - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} -.hi-meta { - font-size: 10px; - color: var(--morandi-text-muted); - margin-top: 2px; -} -.hi-del { - border: none; - background: transparent; - color: #DAB8B0; - font-size: 15px; - cursor: pointer; - padding: 4px 8px; - border-radius: 5px; - transition: background 0.2s; -} -.hi-del:hover { background: var(--morandi-pink-light); } - -/* ═══════════════════════════════════════════════════════════ - Math Life Sub Nav - ═══════════════════════════════════════════════════════════ */ -.life-sub-nav { - display: flex; - gap: 4px; - padding: 6px 12px; - background: var(--morandi-card); - border-bottom: 1px solid var(--morandi-border); -} -.life-sub-btn { - flex: 1; - padding: 7px 0; - border: none; - border-radius: 8px; - background: var(--morandi-bg); - font-size: 12px; - font-weight: 600; - color: var(--morandi-text-sub); - cursor: pointer; - transition: all 0.2s; -} -.life-sub-btn.active { - background: var(--morandi-pink); - color: #fff; - box-shadow: 0 2px 6px rgba(218, 184, 176, 0.3); -} - -/* ═══════════════════════════════════════════════════════════ - About Card - ═══════════════════════════════════════════════════════════ */ -.about-card { - background: var(--morandi-card); - border-radius: var(--radius); - padding: 24px 18px; - text-align: center; - box-shadow: var(--shadow-sm); - margin: 0 12px; -} -.about-card .avatar { - width: 56px; height: 56px; - background: var(--morandi-blue); - border-radius: 50% 50% 50% 50% / 60% 60% 40% 40%; - display: flex; align-items: center; justify-content: center; - font-size: 26px; margin: 0 auto 12px; color: #fff; - box-shadow: 0 3px 10px rgba(160,185,216,0.2), inset 0 -3px 0 rgba(0,0,0,0.08); -} -.about-card h2 { font-size: 16px; font-weight: 700; margin-bottom: 4px; color: var(--morandi-text); } -.about-card .subtitle { font-size: 11px; color: var(--morandi-blue); font-weight: 600; margin-bottom: 12px; } -.about-card .quote { - font-size: 12px; color: var(--morandi-text-sub); line-height: 1.6; - padding: 10px; background: var(--morandi-bg); border-radius: var(--radius-sm); margin-bottom: 10px; -} -.about-card .version { font-size: 10px; color: var(--morandi-text-muted); } - -/* ═══════════════════════════════════════════════════════════ - Responsive - ═══════════════════════════════════════════════════════════ */ -@media (min-width: 480px) { - body::before, - .app-container { - border-radius: 34px; - } - body::before { - top: 10px; - height: calc(100dvh - 20px); - } - .app-container { - margin-top: 10px; - height: calc(100dvh - 20px); - box-shadow: 0 20px 60px rgba(160, 185, 216, 0.12); - border: 1px solid rgba(200, 208, 216, 0.4); - } -} -@media (max-width: 420px) { - body::before { display: none; } - .app-container { width: 100%; max-width: 100%; } -} - -/* ═══════════════════════════════════════════════════════════ - 剧本广场 (Script Square) — v8.1 - ═══════════════════════════════════════════════════════════ */ -.script-square { - padding: 12px 12px 28px; - animation: fadeIn 0.35s ease; -} - -/* 分区标题 */ -.ss-section-title { - font-size: 12px; - font-weight: 700; - color: var(--morandi-text-sub); - margin: 18px 0 10px; - padding: 0 2px; - letter-spacing: 0.8px; - text-transform: uppercase; -} -.ss-section-title:first-of-type { - margin-top: 4px; -} - -/* ═══ 快捷继续卡片 ═══ */ -.ss-continue { - margin-bottom: 4px; -} -.ss-continue-card { - display: flex; - align-items: center; - gap: 12px; - background: var(--morandi-card); - border: 1.5px solid var(--morandi-blue-light); - border-radius: var(--radius-lg); - padding: 14px 16px; - cursor: pointer; - box-shadow: 0 2px 10px rgba(160,185,216,0.12); - transition: transform 0.15s, box-shadow 0.2s; -} -.ss-continue-card:active { - transform: scale(0.98); - box-shadow: var(--shadow); -} -.ss-continue-badge { - padding: 5px 12px; - border-radius: 999px; - background: var(--morandi-blue); - color: #fff; - font-size: 11px; - font-weight: 700; - white-space: nowrap; - flex-shrink: 0; - box-shadow: 0 2px 6px rgba(160,185,216,0.25); -} -.ss-continue-info { - flex: 1; - min-width: 0; -} -.ss-continue-title { - font-size: 14px; - font-weight: 700; - color: var(--morandi-text); - margin-bottom: 2px; -} -.ss-continue-sub { - font-size: 11px; - color: var(--morandi-text-muted); -} -.ss-continue-arrow { - font-size: 20px; - color: var(--morandi-blue); - flex-shrink: 0; -} - -/* ═══ 精选剧本大卡 ═══ */ -.ss-hero-cards { - display: flex; - flex-direction: column; - gap: 14px; -} -.ss-hero-card { - position: relative; - border-radius: var(--radius-xl); - overflow: hidden; - cursor: pointer; - box-shadow: 0 4px 18px rgba(0,0,0,0.06); - transition: transform 0.2s, box-shadow 0.2s; - min-height: 170px; - display: flex; - flex-direction: column; - justify-content: flex-end; -} -.ss-hero-card:active { - transform: scale(0.98); - box-shadow: 0 6px 22px rgba(0,0,0,0.1); -} - -/* 大卡背景层 */ -.ss-hero-bg { - position: absolute; - inset: 0; - z-index: 0; -} -.ss-hero-bg::before { - content: ""; - position: absolute; - top: -30px; right: -30px; - width: 120px; height: 120px; - border-radius: 50%; - background: rgba(255,255,255,0.06); -} -.ss-hero-bg::after { - content: ""; - position: absolute; - bottom: -20px; left: 20px; - width: 80px; height: 80px; - border-radius: 50%; - background: rgba(255,255,255,0.04); -} - -/* 大卡内容层 */ -.ss-hero-content { - position: relative; - z-index: 1; - padding: 22px 18px; - color: #fff; -} -.ss-hero-icon { - font-size: 36px; - margin-bottom: 10px; - opacity: 0.85; -} -.ss-hero-name { - font-size: 19px; - font-weight: 800; - margin-bottom: 4px; - letter-spacing: 0.2px; -} -.ss-hero-desc { - font-size: 12px; - opacity: 0.78; - line-height: 1.5; - margin-bottom: 12px; -} -.ss-hero-meta { - display: flex; - gap: 14px; - font-size: 10px; - opacity: 0.55; -} -.ss-hero-status { - display: inline-flex; - align-items: center; - gap: 4px; - padding: 3px 10px; - border-radius: 999px; - background: rgba(255,255,255,0.18); - font-size: 10px; - font-weight: 600; -} - -/* 各剧本配色 */ -.ss-card-wuxia .ss-hero-bg { - background: linear-gradient(145deg, #7BA89A 0%, #5C8F7E 50%, #8BB5A5 100%); -} -.ss-card-simulator .ss-hero-bg { - background: linear-gradient(145deg, #C9A39A 0%, #B8958A 50%, #D4B5AC 100%); -} -.ss-card-mathbti .ss-hero-bg { - background: linear-gradient(145deg, #8BA4C0 0%, #7A93B0 50%, #9CB5CD 100%); -} - -/* ═══ 即将上线小卡(横向滚动) ═══ */ -.ss-coming-cards { - display: flex; - gap: 12px; - overflow-x: auto; - scrollbar-width: none; - padding-bottom: 6px; -} -.ss-coming-cards::-webkit-scrollbar { display: none; } -.ss-coming-card { - flex-shrink: 0; - width: 130px; - border-radius: var(--radius-lg); - overflow: hidden; - background: var(--morandi-card); - box-shadow: var(--shadow-sm); - opacity: 0.7; - pointer-events: none; -} -.ss-coming-card-bg { - height: 72px; - display: flex; - align-items: center; - justify-content: center; - font-size: 26px; - opacity: 0.35; -} -.ss-coming-card-body { - padding: 10px 12px 12px; -} -.ss-coming-card-name { - font-size: 12px; - font-weight: 700; - color: var(--morandi-text); - margin-bottom: 3px; -} -.ss-coming-card-desc { - font-size: 10px; - color: var(--morandi-text-muted); - line-height: 1.4; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} -.ss-coming-badge { - margin-top: 6px; - display: inline-block; - padding: 2px 8px; - border-radius: 999px; - background: var(--morandi-purple-light); - color: var(--morandi-purple); - font-size: 9px; - font-weight: 600; -} diff --git a/static/css/style.min.css b/static/css/style.min.css deleted file mode 100644 index fc90abb..0000000 --- a/static/css/style.min.css +++ /dev/null @@ -1 +0,0 @@ -:root{--morandi-bg:#F0F2F5;--morandi-card:#FAFBFC;--morandi-card-warm:#F5F0E6;--morandi-card-cool:#EEF2F0;--morandi-card-lilac:#F2EEF5;--morandi-blue:#A0B9D8;--morandi-blue-deep:#8AA3C2;--morandi-blue-light:#DCE6F0;--morandi-pink:#DAB8B0;--morandi-pink-light:#F2E4DF;--morandi-green:#B8D0C9;--morandi-green-light:#E4EFEB;--morandi-purple:#C5CAD0;--morandi-purple-deep:#AEB3B9;--morandi-text:#4A4A4A;--morandi-text-sub:#999999;--morandi-text-muted:#B0B0B0;--morandi-border:#E8EAED;--morandi-divider:#EEEEEE;--module-discover:#A0B9D8;--module-discover-light:#DCE6F0;--module-knowledge:#B8D0C9;--module-knowledge-light:#E4EFEB;--module-life:#DAB8B0;--module-life-light:#F2E4DF;--module-profile:#C5CAD0;--module-profile-light:#EDEEF0;--primary:#A0B9D8;--primary-dark:#8AA3C2;--primary-light:#DCE6F0;--shuren:#A0B9D8;--shuren-light:#DCE6F0;--shuren-gradient:linear-gradient(135deg,#A0B9D8,#8AA3C2);--shushuo:#B8D0C9;--shushuo-light:#E4EFEB;--shushuo-gradient:linear-gradient(135deg,#B8D0C9,#A0BBAF);--shusi:#DAB8B0;--shusi-light:#F2E4DF;--shusi-gradient:linear-gradient(135deg,#DAB8B0,#C9A39A);--mine:#C5CAD0;--mine-light:#EDEEF0;--mine-gradient:linear-gradient(135deg,#C5CAD0,#AEB3B9);--bg:#F0F2F5;--card:#FAFBFC;--text:#4A4A4A;--text-secondary:#999999;--text-muted:#B0B0B0;--border:#E8EAED;--sidebar-bg:#4A4A4A;--sidebar-text:#999999;--sidebar-active:#FAFBFC;--radius-sm:8px;--radius:12px;--radius-lg:16px;--radius-xl:20px;--shadow-sm:0 1px 3px rgba(0,0,0,0.04);--shadow:0 2px 8px rgba(0,0,0,0.05);--shadow-lg:0 4px 16px rgba(0,0,0,0.06);--accent:var(--primary);--accent-light:var(--primary-light);--accent-gradient:linear-gradient(135deg,var(--primary),var(--primary-dark))}*{margin:0;padding:0;box-sizing:border-box}html{scroll-behavior:smooth;-webkit-font-smoothing:antialiased}body{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI","PingFang SC","Microsoft YaHei","Helvetica Neue",sans-serif;background:#E2E5EA;display:flex;justify-content:center;align-items:flex-start;min-height:100vh;color:var(--morandi-text);-webkit-tap-highlight-color:transparent}body::before{content:"";position:fixed;top:0;left:50%;transform:translateX(-50%);width:min(390px,100vw);height:100dvh;background:var(--morandi-bg);z-index:-1;box-shadow:0 0 80px rgba(0,0,0,0.06)}.app-container{display:flex;flex-direction:column;width:min(390px,100vw);max-width:390px;height:100dvh;overflow:hidden;background:var(--morandi-bg);position:relative}.top-bar{display:flex;align-items:center;justify-content:space-between;padding:calc(10px + env(safe-area-inset-top,0px)) 16px 8px;background:var(--morandi-bg);z-index:50}.top-bar-brand{display:flex;align-items:center;gap:8px}.brand-icon-sm{width:28px;height:28px;background:var(--morandi-blue);border-radius:50% 50% 50% 50% / 60% 60% 40% 40%;display:flex;align-items:center;justify-content:center;font-size:16px;color:#fff;font-weight:700;box-shadow:0 2px 6px rgba(160,185,216,0.25),inset 0 -3px 0 rgba(0,0,0,.08)}.brand-text-sm{font-size:15px;font-weight:700;color:var(--morandi-text);letter-spacing:0.3px}.top-bar-actions{display:flex;align-items:center;gap:8px}.icon-btn{width:36px;height:36px;border-radius:50%;border:1px solid var(--morandi-border);background:rgba(250,251,252,0.85);display:flex;align-items:center;justify-content:center;font-size:16px;color:var(--morandi-text-sub);cursor:pointer;box-shadow:var(--shadow-sm);transition:all 0.2s}.icon-btn:active{background:var(--morandi-blue-light);border-color:var(--morandi-blue);color:var(--morandi-blue)}.content-area{flex:1;overflow-y:auto;overflow-x:hidden;background:var(--morandi-bg);display:flex;flex-direction:column}.section-title-bar{padding:12px 16px 10px;background:var(--morandi-bg);position:sticky;top:0;z-index:20}.section-title{font-size:16px;font-weight:700;color:var(--morandi-text);letter-spacing:0.2px}.module-banner{display:flex;align-items:center;gap:10px;padding:12px 14px;margin:0 12px 10px;border-radius:var(--radius);transition:all 0.3s}.module-banner.shuren{background:var(--morandi-blue-light)}.module-banner.shushuo{background:var(--morandi-green-light)}.module-banner.shusi{background:var(--morandi-pink-light)}.module-banner.shuwu{background:var(--module-profile-light)}.banner-icon{font-size:24px;flex-shrink:0;opacity:0.7}.banner-text{flex:1}.banner-title{font-size:12px;font-weight:600;margin-bottom:2px}.module-banner.shuren .banner-title{color:#6B8AAB}.module-banner.shushuo .banner-title{color:#7BA89A}.module-banner.shusi .banner-title{color:#B8958A}.module-banner.shuwu .banner-title{color:#8A8F94}.banner-sub{font-size:11px;color:var(--morandi-text-sub);line-height:1.5}.category-filter{display:flex;flex-wrap:wrap;gap:6px;padding:0 12px 10px}.cat-btn{padding:5px 12px;border-radius:14px;border:1px solid var(--morandi-border);background:var(--morandi-card);font-size:11px;font-weight:500;color:var(--morandi-text-sub);cursor:pointer;transition:all 0.2s;white-space:nowrap;box-shadow:var(--shadow-sm)}.cat-btn:active,.cat-btn.active{border-color:var(--morandi-blue);background:var(--morandi-blue-light);color:var(--morandi-blue);font-weight:600}.save-panel-overlay{position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(74,74,74,0.45);z-index:180;display:none;align-items:center;justify-content:center;backdrop-filter:blur(4px)}.save-panel-overlay.show{display:flex}.save-panel{background:var(--morandi-bg);border-radius:var(--radius-xl);width:min(360px,92vw);max-height:78vh;display:flex;flex-direction:column;overflow:hidden;box-shadow:0 16px 48px rgba(0,0,0,0.12)}.save-panel-header{display:flex;align-items:center;gap:10px;padding:14px 16px;background:var(--morandi-card);border-bottom:1px solid var(--morandi-border);flex-shrink:0}.sp-back{border:none;background:var(--morandi-bg);padding:5px 10px;border-radius:7px;font-size:12px;color:var(--morandi-text-sub);cursor:pointer}.sp-title{font-size:15px;font-weight:700;color:var(--morandi-text);flex:1}.save-slots{flex:1;overflow-y:auto;padding:12px 14px 20px;display:flex;flex-direction:column;gap:10px}.save-slot{background:var(--morandi-card);border-radius:var(--radius);padding:14px 16px;box-shadow:var(--shadow-sm);border:1px solid var(--morandi-border);display:flex;align-items:center;gap:12px;cursor:pointer;transition:transform 0.15s,border-color 0.2s}.save-slot:active{transform:scale(0.98)}.save-slot.active{border-color:var(--morandi-blue);box-shadow:0 2px 12px rgba(160,185,216,0.15)}.save-slot-empty{opacity:0.5;justify-content:center;padding:22px;color:var(--morandi-text-muted);font-size:13px}.save-slot-icon{width:42px;height:42px;border-radius:10px;display:flex;align-items:center;justify-content:center;font-size:20px;flex-shrink:0}.save-slot-info{flex:1;min-width:0}.save-slot-name{font-size:13px;font-weight:700;color:var(--morandi-text);margin-bottom:3px}.save-slot-meta{font-size:10px;color:var(--morandi-text-muted);display:flex;gap:8px}.save-slot-actions{display:flex;gap:6px;flex-shrink:0}.save-slot-btn{padding:5px 10px;border:1px solid var(--morandi-border);border-radius:7px;background:var(--morandi-bg);font-size:10px;font-weight:600;cursor:pointer;color:var(--morandi-text-sub);white-space:nowrap;transition:all 0.15s}.save-slot-btn.primary{background:var(--morandi-blue);color:#fff;border-color:transparent}.save-slot-btn:active{transform:scale(0.95)}.gallery-overlay{position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(74,74,74,0.45);z-index:180;display:none;align-items:center;justify-content:center;backdrop-filter:blur(4px)}.gallery-overlay.show{display:flex}.gallery-panel{background:var(--morandi-bg);border-radius:var(--radius-xl);width:min(360px,92vw);max-height:80vh;display:flex;flex-direction:column;overflow:hidden;box-shadow:0 16px 48px rgba(0,0,0,0.12)}.gallery-header{display:flex;align-items:center;gap:10px;padding:14px 16px;background:var(--morandi-card);border-bottom:1px solid var(--morandi-border);flex-shrink:0}.gallery-progress{font-size:12px;font-weight:600;color:var(--morandi-blue);white-space:nowrap}.gallery-grid{flex:1;overflow-y:auto;padding:16px 14px 24px;display:grid;grid-template-columns:1fr 1fr;gap:10px}.gallery-card{background:var(--morandi-card);border-radius:var(--radius);padding:18px 14px;text-align:center;box-shadow:var(--shadow-sm);display:flex;flex-direction:column;align-items:center;gap:8px;transition:transform 0.15s}.gallery-card.unlocked{cursor:pointer}.gallery-card.unlocked:active{transform:scale(0.96)}.gallery-card.locked{opacity:0.4;filter:grayscale(1)}.gallery-badge{font-size:32px}.gallery-card-name{font-size:12px;font-weight:700;color:var(--morandi-text)}.gallery-card-desc{font-size:10px;color:var(--morandi-text-muted);line-height:1.4}.gallery-card-lock{font-size:20px;opacity:0.5}.search-header{padding:10px 14px 4px;background:var(--morandi-bg);position:sticky;top:0;z-index:25}.search-bar-wrap{display:flex;align-items:center;background:var(--morandi-card);padding:9px 14px;border-radius:22px;box-shadow:0 1px 4px rgba(0,0,0,0.04);border:1px solid var(--morandi-border);transition:border-color 0.2s,box-shadow 0.2s}.search-bar-wrap:focus-within{border-color:var(--morandi-blue);box-shadow:0 1px 8px rgba(160,185,216,0.15)}.search-icon{margin-right:8px;font-size:13px;opacity:0.35}.search-bar-wrap input{flex:1;border:none;background:transparent;outline:none;font-size:13px;color:var(--morandi-text)}.search-bar-wrap input::placeholder{color:var(--morandi-text-muted)}.sub-nav{display:flex;gap:22px;padding:6px 16px 8px;background:var(--morandi-bg);border-bottom:1px solid var(--morandi-border);margin-bottom:10px}.sub-nav-btn{border:none;background:transparent;font-size:13px;font-weight:500;color:var(--morandi-text-sub);cursor:pointer;padding:4px 0;position:relative;transition:color 0.2s;letter-spacing:0.2px}.sub-nav-btn:active{color:var(--morandi-text)}.sub-nav-btn.active{color:var(--morandi-text);font-weight:700}.sub-nav-btn.active::after{content:"";position:absolute;bottom:0;left:50%;transform:translateX(-50%);width:18px;height:3px;background:var(--morandi-blue);border-radius:2px}.discipline-grid-container{padding:14px 14px 20px;animation:fadeIn 0.3s ease}.grid-section-title{font-size:15px;font-weight:700;color:var(--morandi-text);margin-bottom:16px;letter-spacing:0.2px}.discipline-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:12px 10px}.discipline-item{display:flex;flex-direction:column;align-items:center;gap:8px;cursor:pointer;transition:transform 0.2s;padding:16px 6px 12px;border-radius:var(--radius-lg);background:var(--morandi-card);box-shadow:var(--shadow-sm)}.discipline-item:active{transform:scale(0.95);box-shadow:var(--shadow)}.discipline-icon-box{width:56px;height:56px;border-radius:16px;display:flex;align-items:center;justify-content:center;font-size:24px;font-weight:700;transition:all 0.2s}.discipline-item:hover .discipline-icon-box{transform:translateY(-2px);box-shadow:var(--shadow)}.discipline-label{font-size:11px;font-weight:600;color:var(--morandi-text);text-align:center;white-space:nowrap;letter-spacing:0.2px}.discipline-active-header{padding:10px 14px;background:var(--morandi-bg);border-bottom:1px solid var(--morandi-border);margin-bottom:10px;display:flex;align-items:center}.back-to-grid{display:flex;align-items:center;gap:8px;border:none;background:transparent;cursor:pointer;color:var(--morandi-text);font-size:14px;font-weight:700;padding:4px 0}.back-icon{font-size:16px;color:var(--morandi-blue)}.video-list{display:grid;grid-template-columns:1fr 1fr;gap:10px;padding:0 12px 20px}.video-card{background:var(--morandi-card);border-radius:var(--radius);overflow:hidden;box-shadow:var(--shadow-sm);cursor:pointer;transition:all 0.2s;display:flex;flex-direction:column}.video-card:active{transform:scale(0.98);box-shadow:var(--shadow)}.vc-cover{width:100%;aspect-ratio:16/10;position:relative;display:flex;align-items:center;justify-content:center;overflow:hidden}.vc-cover-placeholder{font-size:28px;opacity:0.18}.vc-duration{position:absolute;bottom:6px;right:6px;background:rgba(74,74,74,0.55);backdrop-filter:blur(4px);color:#fff;font-size:10px;font-weight:500;padding:2px 6px;border-radius:5px;letter-spacing:0.3px}.vc-content{padding:8px 10px 10px;flex:1;display:flex;flex-direction:column}.vc-title{font-size:12px;font-weight:600;line-height:1.45;color:var(--morandi-text);margin-bottom:5px;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;min-height:34px;letter-spacing:0.1px}.vc-info-row{display:flex;align-items:center;gap:5px;margin-top:auto}.vc-author-avatar{width:16px;height:16px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:8px;color:#fff;flex-shrink:0}.vc-author-name{font-size:10px;color:var(--morandi-text-sub);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;flex:1}.vc-stat{font-size:10px;color:var(--morandi-text-muted);display:flex;align-items:center;gap:2px}.vc-fav{position:absolute;top:6px;right:6px;width:26px;height:26px;border-radius:50%;border:none;background:rgba(74,74,74,0.28);backdrop-filter:blur(3px);color:#fff;font-size:13px;cursor:pointer;display:flex;align-items:center;justify-content:center;opacity:0;transition:opacity 0.2s,transform 0.15s;z-index:2}.video-card:hover .vc-fav,.video-card:active .vc-fav{opacity:1}.vc-fav.active{opacity:1;background:rgba(160,185,216,0.75)}.vc-fav:active{transform:scale(1.15)}.loading{display:flex;flex-direction:column;align-items:center;padding:60px 0;color:var(--morandi-text-muted)}.spinner{width:28px;height:28px;border:2.5px solid var(--morandi-border);border-top-color:var(--morandi-blue);border-radius:50%;animation:spin 0.7s linear infinite;margin-bottom:10px}@keyframes spin{to{transform:rotate(360deg)}}.empty-state{text-align:center;padding:60px 20px;color:var(--morandi-text-muted)}.empty-state .icon{font-size:40px;margin-bottom:10px;opacity:0.4}.empty-state h3{font-size:14px;color:var(--morandi-text-sub);margin-bottom:4px;font-weight:600}.empty-state p{font-size:12px}.shushuo-hero{padding:0 12px 14px;animation:fadeIn 0.3s ease}.hero-card{background:linear-gradient(145deg,#C0CAD4 0%,#A8B8C8 60%,#B5C3D0 100%);border-radius:var(--radius-lg);padding:22px 18px;color:#fff;position:relative;overflow:hidden;box-shadow:0 4px 16px rgba(192,202,212,0.2)}.hero-card::before{content:"";position:absolute;top:-20px;right:-20px;width:90px;height:90px;border-radius:50%;background:rgba(255,255,255,0.05)}.hero-card::after{content:"";position:absolute;bottom:-25px;left:-10px;width:70px;height:70px;border-radius:50%;background:rgba(255,255,255,0.03)}.hero-badge{display:inline-block;padding:3px 10px;background:rgba(255,255,255,0.15);border-radius:10px;font-size:10px;font-weight:600;margin-bottom:12px;letter-spacing:0.5px}.hero-title{font-size:19px;font-weight:800;line-height:1.35;margin-bottom:6px;position:relative;z-index:1}.hero-desc{font-size:11px;opacity:0.72;line-height:1.5;margin-bottom:12px;position:relative;z-index:1}.hero-stats{display:flex;gap:16px;font-size:10px;opacity:0.55;position:relative;z-index:1}.shushuo-topics{padding:0 12px 8px;overflow:hidden}.topics-scroll{display:flex;gap:7px;overflow-x:auto;scrollbar-width:none;padding-bottom:4px}.topics-scroll::-webkit-scrollbar{display:none}.topic-pill{display:inline-flex;padding:5px 12px;border-radius:14px;border:1px solid var(--morandi-border);background:var(--morandi-card);font-size:11px;font-weight:500;color:var(--morandi-text-sub);cursor:pointer;white-space:nowrap;flex-shrink:0;transition:all 0.2s}.topic-pill:active,.topic-pill.active{border-color:var(--morandi-green);background:var(--morandi-green-light);color:#7BA89A;font-weight:600}.shushuo-feed{display:flex;flex-direction:column;gap:12px;padding:0 12px 20px;animation:fadeIn 0.4s ease}.feed-card{background:var(--morandi-card);border-radius:var(--radius-lg);overflow:hidden;box-shadow:var(--shadow-sm);cursor:pointer;transition:transform 0.2s}.feed-card:active{transform:scale(0.98)}.feed-card-visual{width:100%;height:132px;position:relative;display:flex;align-items:center;justify-content:center;overflow:hidden}.feed-card-icon{font-size:42px;opacity:0.22;z-index:1}.feed-card-question{position:absolute;bottom:10px;left:14px;right:14px;font-size:14px;font-weight:700;color:#fff;z-index:1;text-shadow:0 1px 2px rgba(0,0,0,0.15);line-height:1.4}.feed-card-body{padding:10px 14px 14px}.feed-card-title{font-size:15px;font-weight:700;line-height:1.4;margin-bottom:5px;color:var(--morandi-text)}.feed-card-desc{font-size:12px;color:var(--morandi-text-sub);line-height:1.6;margin-bottom:8px}.feed-card-footer{display:flex;align-items:center;gap:8px}.feed-card-topic{font-size:10px;font-weight:500}.feed-card-time{font-size:10px;color:var(--morandi-text-muted)}.feed-card-fav{position:absolute;top:6px;right:6px;width:26px;height:26px;border-radius:50%;border:none;background:rgba(74,74,74,0.2);color:#fff;font-size:13px;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:transform 0.15s;z-index:2}.feed-card-fav.active{background:rgba(160,185,216,0.7)}.feed-card-fav:active{transform:scale(1.15)}.profile-page{padding:6px 12px 20px;animation:fadeIn 0.3s ease;display:flex;flex-direction:column;gap:6px}.profile-card{background:linear-gradient(135deg,#C5CAD0 0%,#B5C0CA 40%,#C0C8D0 100%);border-radius:var(--radius-lg);padding:14px 14px 12px;color:#fff;box-shadow:0 6px 20px rgba(197,202,208,0.2);flex-shrink:0;cursor:pointer;transition:opacity 0.15s}.profile-card:active{opacity:0.92}.profile-main{display:flex;align-items:center;gap:10px}.profile-avatar{width:48px;height:48px;border-radius:50% 50% 50% 50% / 60% 60% 40% 40%;box-shadow:inset 0 -3px 0 rgba(0,0,0,.08);display:flex;align-items:center;justify-content:center;background:rgba(255,255,255,0.2);font-size:20px;font-weight:800;flex-shrink:0}.profile-meta{flex:1;min-width:0}.profile-name{font-size:16px;font-weight:800;margin-bottom:1px;letter-spacing:0.2px}.profile-id{font-size:10px;opacity:0.7;margin-bottom:6px}.profile-tags{display:flex;flex-wrap:wrap;gap:5px}.profile-tags span{padding:2px 7px;border-radius:999px;background:rgba(255,255,255,0.16);font-size:10px;font-weight:500}.profile-edit{margin-top:8px;width:fit-content;padding:4px 10px;border-radius:999px;background:rgba(255,255,255,0.14);font-size:10px;font-weight:600;cursor:pointer}.profile-stats{display:grid;grid-template-columns:repeat(3,1fr);gap:6px;flex-shrink:0}.profile-stat{background:var(--morandi-card);border-radius:var(--radius);padding:10px 6px;text-align:center;box-shadow:var(--shadow-sm);border:1px solid var(--morandi-border);cursor:pointer;transition:transform 0.15s}.profile-stat:active{transform:scale(0.96)}.profile-stat-num{font-size:17px;font-weight:800;color:var(--morandi-blue);margin-bottom:1px}.profile-stat-label{font-size:10px;color:var(--morandi-text-sub)}.profile-section{flex-shrink:0}.profile-section-title{font-size:10px;font-weight:700;color:var(--morandi-text-sub);margin-bottom:4px;padding:0 2px;letter-spacing:0.3px;text-transform:uppercase}.profile-menu-card{background:var(--morandi-card);border-radius:var(--radius);box-shadow:var(--shadow-sm);overflow:hidden}.profile-menu-item{display:flex;align-items:center;justify-content:space-between;padding:9px 12px;border-bottom:1px solid var(--morandi-divider);cursor:pointer;transition:background 0.15s}.profile-menu-item:last-child{border-bottom:none}.profile-menu-item:active{background:var(--morandi-bg)}.profile-menu-left{display:flex;align-items:center;gap:8px;font-size:12px;font-weight:600;color:var(--morandi-text)}.menu-icon{width:22px;height:22px;border-radius:6px;background:var(--morandi-bg);display:inline-flex;align-items:center;justify-content:center;color:var(--morandi-blue);font-size:11px;font-weight:700}.menu-arrow{font-size:14px;color:var(--morandi-text-muted);font-weight:400}.bottom-tab-bar{display:flex;align-items:center;justify-content:space-around;gap:2px;background:rgba(255,255,255,0.96);backdrop-filter:blur(20px);-webkit-backdrop-filter:blur(20px);padding:8px 10px calc(10px + env(safe-area-inset-bottom,0px));border-top:1px solid var(--morandi-border);z-index:60;box-shadow:0 -4px 20px rgba(160,185,216,0.06)}.tab-btn{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:3px;border:none;background:transparent;color:var(--morandi-text-muted);cursor:pointer;width:78px;min-height:48px;border-radius:14px;transition:all 0.2s ease}.tab-icon{width:20px;height:20px;position:relative;transition:transform 0.2s,opacity 0.2s;opacity:0.6}.tab-label{font-size:10px;font-weight:500;letter-spacing:0.2px}.tab-btn.active{color:var(--morandi-blue);background:var(--morandi-blue-light)}.tab-btn.active .tab-icon{transform:translateY(-1px);opacity:1}.tab-btn.active .tab-label{font-weight:700}.tab-icon::before,.tab-icon::after{content:"";position:absolute;box-sizing:border-box}.tab-icon-discover::before{inset:3px;border:1.8px solid currentColor;border-radius:5px}.tab-icon-discover::after{width:4px;height:8px;right:5px;top:7px;background:currentColor;border-radius:2px}.tab-icon-knowledge::before{left:4px;right:4px;bottom:3px;height:10px;border:1.8px solid currentColor;border-top:none;border-radius:1px 1px 4px 4px}.tab-icon-knowledge::after{left:6px;top:2px;width:10px;height:10px;background:currentColor;clip-path:polygon(50% 0,100% 40%,100% 100%,0 100%,0 40%)}.tab-icon-life::before{left:5px;right:5px;top:3px;bottom:3px;border:1.8px solid currentColor;border-radius:3px 3px 7px 7px}.tab-icon-life::after{left:8px;right:8px;top:8px;height:2px;background:currentColor;border-radius:2px}.tab-icon-mine::before{width:7px;height:7px;left:3px;top:3px;background:currentColor;border-radius:2px;box-shadow:9px 0 0 currentColor,0 9px 0 currentColor,9px 9px 0 currentColor}.chat-overlay{display:none;position:fixed;inset:0;z-index:200;background:rgba(74,74,74,0.35);justify-content:center;align-items:center;backdrop-filter:blur(4px)}.chat-overlay.show{display:flex}.chat-panel{width:380px;max-width:93vw;height:78vh;background:var(--morandi-card);border-radius:var(--radius-xl);display:flex;flex-direction:column;animation:modalIn 0.25s cubic-bezier(0.16,1,0.3,1);box-shadow:0 20px 50px rgba(0,0,0,0.12);overflow:hidden}@keyframes modalIn{from{opacity:0;transform:scale(0.95) translateY(10px)}to{opacity:1;transform:scale(1) translateY(0)}}.chat-header{display:flex;align-items:center;justify-content:space-between;padding:14px 18px;border-bottom:1px solid var(--morandi-border);font-weight:700;font-size:14px;color:var(--morandi-text)}.chat-header-left{display:flex;align-items:center;gap:10px}.ai-avatar{width:30px;height:30px;background:var(--morandi-blue);border-radius:9px;display:flex;align-items:center;justify-content:center;font-size:15px;color:#fff;box-shadow:0 2px 6px rgba(160,185,216,0.3)}.chat-close{width:28px;height:28px;border-radius:50%;border:none;background:var(--morandi-bg);cursor:pointer;font-size:14px;display:flex;align-items:center;justify-content:center;color:var(--morandi-text-sub)}.chat-messages{flex:1;overflow-y:auto;padding:14px;display:flex;flex-direction:column;gap:10px;background:var(--morandi-bg)}.chat-msg{display:flex;animation:msgIn 0.3s ease}@keyframes msgIn{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:translateY(0)}}.chat-msg.assistant{justify-content:flex-start}.chat-msg.user{justify-content:flex-end}.msg-bubble{max-width:82%;padding:9px 13px;border-radius:13px;font-size:13px;line-height:1.6;word-break:break-word}.chat-msg.assistant .msg-bubble{background:#fff;border-bottom-left-radius:3px;box-shadow:var(--shadow-sm)}.chat-msg.user .msg-bubble{background:linear-gradient(135deg,#A0B9D8,#8AA3C2);color:#fff;border-bottom-right-radius:3px}.quick-asks{display:flex;flex-wrap:wrap;gap:5px;padding:6px 14px}.quick-ask{padding:5px 11px;border-radius:12px;border:1px solid var(--morandi-border);background:#fff;font-size:11px;color:var(--morandi-text-sub);cursor:pointer;transition:all 0.15s}.quick-ask:active{background:var(--morandi-blue-light);border-color:var(--morandi-blue);color:var(--morandi-blue)}.chat-input-area{display:flex;padding:8px 14px 12px;border-top:1px solid var(--morandi-border);gap:8px;background:#fff}.chat-input-area input{flex:1;padding:9px 13px;border:1px solid var(--morandi-border);border-radius:18px;font-size:13px;outline:none;background:var(--morandi-bg);transition:border-color 0.2s}.chat-input-area input:focus{border-color:var(--morandi-blue)}.send-btn{min-width:36px;height:36px;border:none;border-radius:50%;background:var(--morandi-blue);color:#fff;font-size:15px;cursor:pointer;flex-shrink:0;display:flex;align-items:center;justify-content:center;box-shadow:0 2px 8px rgba(160,185,216,0.25)}.send-btn:active{transform:scale(0.93)}.typing-indicator{display:flex;gap:4px;padding:3px 0}.typing-indicator span{width:5px;height:5px;border-radius:50%;background:var(--morandi-text-muted);animation:bounce 1.2s infinite}.typing-indicator span:nth-child(2){animation-delay:0.2s}.typing-indicator span:nth-child(3){animation-delay:0.4s}@keyframes bounce{0%,60%,100%{transform:translateY(0)}30%{transform:translateY(-6px)}}.video-modal{display:none;position:fixed;inset:0;z-index:200;background:rgba(74,74,74,0.5);justify-content:center;align-items:center;backdrop-filter:blur(4px)}.video-modal.show{display:flex}.video-modal-content{width:360px;max-width:93vw;max-height:86vh;background:var(--morandi-card);border-radius:var(--radius-lg);overflow-y:auto;animation:modalIn 0.25s cubic-bezier(0.16,1,0.3,1)}.video-modal-header{display:flex;justify-content:space-between;align-items:center;padding:14px 16px;border-bottom:1px solid var(--morandi-border)}.video-modal-header h3{font-size:14px;font-weight:700;flex:1;margin-right:10px;line-height:1.4;color:var(--morandi-text)}.video-modal-header button{width:28px;height:28px;border-radius:50%;border:none;background:var(--morandi-bg);cursor:pointer;font-size:14px;display:flex;align-items:center;justify-content:center;flex-shrink:0;color:var(--morandi-text-sub)}.video-player-area{background:#E8EBEF;aspect-ratio:16/9;display:flex;align-items:center;justify-content:center}.video-placeholder{text-align:center;color:rgba(74,74,74,0.35)}.play-icon{width:46px;height:46px;border-radius:50%;background:rgba(160,185,216,0.25);display:flex;align-items:center;justify-content:center;font-size:20px;margin:0 auto 8px}.video-placeholder p{font-size:11px}.video-info{padding:12px 16px}.video-desc{font-size:12px;line-height:1.6;color:var(--morandi-text-sub);margin-bottom:8px}.video-meta{font-size:10px;color:var(--morandi-text-muted);display:flex;flex-wrap:wrap;gap:10px}.video-actions{padding:0 16px 16px;display:flex;gap:8px}.btn-primary,.btn-secondary{flex:1;padding:9px;border-radius:10px;border:none;font-size:12px;font-weight:600;cursor:pointer;text-align:center;transition:transform 0.15s}.btn-primary:active,.btn-secondary:active{transform:scale(0.97)}.btn-primary{background:var(--morandi-blue);color:#fff;box-shadow:0 2px 8px rgba(160,185,216,0.2)}.btn-secondary{background:var(--morandi-bg);color:var(--morandi-text)}.vn-header{display:flex;flex-direction:column;gap:8px;margin-bottom:10px;padding:0 12px}.vn-chapter-badge{align-self:flex-start;padding:4px 12px;border-radius:999px;font-size:11px;font-weight:700;color:#fff;background:var(--morandi-green);box-shadow:0 2px 6px rgba(184,208,201,0.3);letter-spacing:0.3px}.vn-stats{display:flex;gap:6px;flex-wrap:wrap}.stat-badge{font-size:10px;padding:4px 8px;border-radius:10px;color:#fff;font-weight:600;box-shadow:0 1px 3px rgba(0,0,0,0.06)}.vn-actions{display:flex;gap:6px;padding:0 12px}.vn-btn{background:var(--morandi-card);border:1px solid var(--morandi-border);padding:5px 10px;border-radius:8px;font-size:11px;cursor:pointer;box-shadow:var(--shadow-sm);color:var(--morandi-text-sub);font-weight:600;transition:all 0.2s}.vn-btn:active{background:var(--morandi-green-light);border-color:var(--morandi-green);color:#7BA89A}.story-scene{background:var(--morandi-card);margin:0 12px 12px;border-radius:var(--radius-lg);padding:18px;min-height:260px;box-shadow:var(--shadow-sm);position:relative;overflow:hidden;display:flex;flex-direction:column;justify-content:flex-end}.vn-background{position:absolute;top:0;left:0;width:100%;height:100%;object-fit:cover;z-index:0;border-radius:var(--radius-lg)}.vn-sprite{position:absolute;bottom:0;right:0;max-height:75%;max-width:55%;object-fit:contain;z-index:1;pointer-events:none;transition:opacity 0.4s ease}.story-text-box{position:relative;z-index:2;background:rgba(250,251,252,0.94);backdrop-filter:blur(4px);padding:12px;border-radius:9px;border:1px solid var(--morandi-border)}.story-character{font-weight:800;color:var(--morandi-green);margin-bottom:5px;font-size:12px;letter-spacing:0.3px}.story-text{font-size:13px;line-height:1.7;color:var(--morandi-text);min-height:44px;white-space:pre-line}.story-choices{display:flex;flex-direction:column;gap:8px;padding:0 12px 20px}.choice-btn{background:var(--morandi-card);border:1px solid var(--morandi-border);border-radius:var(--radius);padding:12px 14px;font-size:13px;font-weight:600;color:var(--morandi-text);text-align:left;cursor:pointer;transition:all 0.2s;box-shadow:var(--shadow-sm);display:flex;flex-direction:column;gap:5px;animation:fadeInUp 0.3s ease both}.choice-btn:hover{background:var(--morandi-green-light);border-color:var(--morandi-green)}.choice-btn:active{transform:scale(0.98);background:var(--morandi-green-light)}.choice-hint{font-size:10px;color:#7BA89A;font-weight:500}.choice-text{line-height:1.5}@keyframes fadeInUp{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}@keyframes fadeIn{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}.sim-header{display:flex;flex-direction:column;gap:8px;margin:10px 0;padding:0 12px}.sim-year-badge{align-self:flex-start;padding:4px 14px;border-radius:999px;font-size:11px;font-weight:700;color:#fff;box-shadow:0 2px 6px rgba(0,0,0,0.1)}.sim-stats{display:flex;gap:6px;flex-wrap:wrap}.sim-stat-bar{font-size:10px;padding:4px 8px;border-radius:10px;color:#fff;font-weight:600;box-shadow:0 1px 3px rgba(0,0,0,0.06)}.sim-actions-top{display:flex;gap:6px;padding:0 12px 8px}.sim-scene{background:var(--morandi-card);margin:0 12px 12px;border-radius:var(--radius-lg);padding:18px;min-height:180px;box-shadow:var(--shadow-sm);position:relative}.sim-character{font-weight:800;color:var(--morandi-pink);margin-bottom:8px;font-size:13px;letter-spacing:0.3px}.sim-text{font-size:13px;line-height:1.7;color:var(--morandi-text);min-height:54px;white-space:pre-line}.sim-choices{display:flex;flex-direction:column;gap:8px;padding:0 12px 28px}.sim-choice-btn{background:var(--morandi-card);border:1px solid var(--morandi-border);border-radius:var(--radius);padding:12px 14px;font-size:13px;font-weight:600;color:var(--morandi-text);text-align:left;cursor:pointer;transition:all 0.2s;box-shadow:var(--shadow-sm);display:flex;flex-direction:column;gap:5px;animation:fadeInUp 0.3s ease both}.sim-choice-btn:hover{background:var(--morandi-pink-light);border-color:var(--morandi-pink)}.sim-choice-btn:active{transform:scale(0.98)}.sim-hint{font-size:10px;color:#B8958A;font-weight:500}.sim-hint-tip{font-size:10px;color:var(--morandi-text-muted);font-weight:400}.sim-choice-text{line-height:1.5}.ending-overlay{position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(74,74,74,0.5);z-index:200;display:none;align-items:center;justify-content:center}.ending-overlay.show{display:flex}.ending-card{background:var(--morandi-card);border-radius:var(--radius-xl);padding:24px 22px;width:min(320px,88vw);text-align:center;box-shadow:0 16px 48px rgba(0,0,0,0.15);animation:fadeInUp 0.4s ease;display:flex;flex-direction:column;gap:10px}.ending-badge{font-size:44px}.ending-type{font-size:12px;color:var(--morandi-text-muted);font-weight:600;letter-spacing:1px}.ending-title{font-size:16px;font-weight:700;color:var(--morandi-text)}.ending-scene{font-size:12px;color:var(--morandi-text-sub);line-height:1.7;background:var(--morandi-bg);border-radius:var(--radius);padding:10px}.avatar-picker-overlay{position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(74,74,74,0.45);z-index:150;display:none;align-items:center;justify-content:center}.avatar-picker-overlay.show{display:flex}.avatar-picker-card{background:var(--morandi-card);border-radius:var(--radius-xl);padding:18px;width:min(320px,88vw);box-shadow:0 16px 48px rgba(0,0,0,0.15);display:flex;flex-direction:column;gap:12px}.picker-title{font-size:14px;font-weight:700;text-align:center;color:var(--morandi-text)}.picker-emoji-grid{display:flex;flex-wrap:wrap;gap:5px;justify-content:center}.picker-emoji-btn{width:34px;height:34px;border:1.5px solid var(--morandi-border);border-radius:8px;background:var(--morandi-bg);font-size:16px;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:all 0.15s}.picker-emoji-btn:active{transform:scale(0.9)}.picker-color-row{display:flex;gap:8px;justify-content:center}.picker-color-btn{width:30px;height:30px;border:none;border-radius:50%;cursor:pointer;transition:transform 0.15s}.picker-color-btn:active{transform:scale(0.85)}.picker-actions{display:flex;gap:8px}.picker-btn{flex:1;padding:9px;border:1px solid var(--morandi-border);border-radius:10px;background:var(--morandi-bg);font-size:12px;font-weight:600;cursor:pointer;text-align:center;color:var(--morandi-text-sub)}.picker-btn.primary{background:var(--morandi-blue);color:#fff;border-color:transparent;box-shadow:0 2px 8px rgba(160,185,216,0.2)}.profile-list-panel{position:absolute;top:0;left:0;right:0;bottom:0;background:var(--morandi-bg);z-index:30;display:flex;flex-direction:column;overflow:hidden}.plp-header{display:flex;align-items:center;gap:10px;padding:12px 14px;background:var(--morandi-card);border-bottom:1px solid var(--morandi-border);position:sticky;top:0;z-index:5}.plp-back{border:none;background:var(--morandi-bg);padding:5px 10px;border-radius:7px;font-size:12px;color:var(--morandi-text-sub);cursor:pointer}.plp-title{font-size:14px;font-weight:700;color:var(--morandi-text)}.plp-content{flex:1;overflow-y:auto;padding:10px 12px}.history-item{display:flex;align-items:center;gap:10px;background:var(--morandi-card);border-radius:var(--radius);padding:10px 12px;margin-bottom:7px;box-shadow:var(--shadow-sm)}.hi-icon{width:40px;height:40px;border-radius:8px;display:flex;align-items:center;justify-content:center;font-size:18px;flex-shrink:0}.hi-info{flex:1;min-width:0}.hi-title{font-size:13px;font-weight:600;color:var(--morandi-text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.hi-meta{font-size:10px;color:var(--morandi-text-muted);margin-top:2px}.hi-del{border:none;background:transparent;color:#DAB8B0;font-size:15px;cursor:pointer;padding:4px 8px;border-radius:5px;transition:background 0.2s}.hi-del:hover{background:var(--morandi-pink-light)}.life-sub-nav{display:flex;gap:4px;padding:6px 12px;background:var(--morandi-card);border-bottom:1px solid var(--morandi-border)}.life-sub-btn{flex:1;padding:7px 0;border:none;border-radius:8px;background:var(--morandi-bg);font-size:12px;font-weight:600;color:var(--morandi-text-sub);cursor:pointer;transition:all 0.2s}.life-sub-btn.active{background:var(--morandi-pink);color:#fff;box-shadow:0 2px 6px rgba(218,184,176,0.3)}.about-card{background:var(--morandi-card);border-radius:var(--radius);padding:24px 18px;text-align:center;box-shadow:var(--shadow-sm);margin:0 12px}.about-card .avatar{width:56px;height:56px;background:var(--morandi-blue);border-radius:50% 50% 50% 50% / 60% 60% 40% 40%;display:flex;align-items:center;justify-content:center;font-size:26px;margin:0 auto 12px;color:#fff;box-shadow:0 3px 10px rgba(160,185,216,0.2),inset 0 -3px 0 rgba(0,0,0,.08)}.about-card h2{font-size:16px;font-weight:700;margin-bottom:4px;color:var(--morandi-text)}.about-card .subtitle{font-size:11px;color:var(--morandi-blue);font-weight:600;margin-bottom:12px}.about-card .quote{font-size:12px;color:var(--morandi-text-sub);line-height:1.6;padding:10px;background:var(--morandi-bg);border-radius:var(--radius-sm);margin-bottom:10px}.about-card .version{font-size:10px;color:var(--morandi-text-muted)}@media (min-width:480px){body::before,.app-container{border-radius:34px}body::before{top:10px;height:calc(100dvh - 20px)}.app-container{margin-top:10px;height:calc(100dvh - 20px);box-shadow:0 20px 60px rgba(160,185,216,0.12);border:1px solid rgba(200,208,216,0.4)}}@media (max-width:420px){body::before{display:none}.app-container{width:100%;max-width:100%}}.script-square{padding:12px 12px 28px;animation:fadeIn 0.35s ease}.ss-section-title{font-size:12px;font-weight:700;color:var(--morandi-text-sub);margin:18px 0 10px;padding:0 2px;letter-spacing:0.8px;text-transform:uppercase}.ss-section-title:first-of-type{margin-top:4px}.ss-continue{margin-bottom:4px}.ss-continue-card{display:flex;align-items:center;gap:12px;background:var(--morandi-card);border:1.5px solid var(--morandi-blue-light);border-radius:var(--radius-lg);padding:14px 16px;cursor:pointer;box-shadow:0 2px 10px rgba(160,185,216,0.12);transition:transform 0.15s,box-shadow 0.2s}.ss-continue-card:active{transform:scale(0.98);box-shadow:var(--shadow)}.ss-continue-badge{padding:5px 12px;border-radius:999px;background:var(--morandi-blue);color:#fff;font-size:11px;font-weight:700;white-space:nowrap;flex-shrink:0;box-shadow:0 2px 6px rgba(160,185,216,0.25)}.ss-continue-info{flex:1;min-width:0}.ss-continue-title{font-size:14px;font-weight:700;color:var(--morandi-text);margin-bottom:2px}.ss-continue-sub{font-size:11px;color:var(--morandi-text-muted)}.ss-continue-arrow{font-size:20px;color:var(--morandi-blue);flex-shrink:0}.ss-hero-cards{display:flex;flex-direction:column;gap:14px}.ss-hero-card{position:relative;border-radius:var(--radius-xl);overflow:hidden;cursor:pointer;box-shadow:0 4px 18px rgba(0,0,0,0.06);transition:transform 0.2s,box-shadow 0.2s;min-height:170px;display:flex;flex-direction:column;justify-content:flex-end}.ss-hero-card:active{transform:scale(0.98);box-shadow:0 6px 22px rgba(0,0,0,0.1)}.ss-hero-bg{position:absolute;inset:0;z-index:0}.ss-hero-bg::before{content:"";position:absolute;top:-30px;right:-30px;width:120px;height:120px;border-radius:50%;background:rgba(255,255,255,0.06)}.ss-hero-bg::after{content:"";position:absolute;bottom:-20px;left:20px;width:80px;height:80px;border-radius:50%;background:rgba(255,255,255,0.04)}.ss-hero-content{position:relative;z-index:1;padding:22px 18px;color:#fff}.ss-hero-icon{font-size:36px;margin-bottom:10px;opacity:0.85}.ss-hero-name{font-size:19px;font-weight:800;margin-bottom:4px;letter-spacing:0.2px}.ss-hero-desc{font-size:12px;opacity:0.78;line-height:1.5;margin-bottom:12px}.ss-hero-meta{display:flex;gap:14px;font-size:10px;opacity:0.55}.ss-hero-status{display:inline-flex;align-items:center;gap:4px;padding:3px 10px;border-radius:999px;background:rgba(255,255,255,0.18);font-size:10px;font-weight:600}.ss-card-wuxia .ss-hero-bg{background:linear-gradient(145deg,#7BA89A 0%,#5C8F7E 50%,#8BB5A5 100%)}.ss-card-simulator .ss-hero-bg{background:linear-gradient(145deg,#C9A39A 0%,#B8958A 50%,#D4B5AC 100%)}.ss-card-mathbti .ss-hero-bg{background:linear-gradient(145deg,#8BA4C0 0%,#7A93B0 50%,#9CB5CD 100%)}.ss-coming-cards{display:flex;gap:12px;overflow-x:auto;scrollbar-width:none;padding-bottom:6px}.ss-coming-cards::-webkit-scrollbar{display:none}.ss-coming-card{flex-shrink:0;width:130px;border-radius:var(--radius-lg);overflow:hidden;background:var(--morandi-card);box-shadow:var(--shadow-sm);opacity:0.7;pointer-events:none}.ss-coming-card-bg{height:72px;display:flex;align-items:center;justify-content:center;font-size:26px;opacity:0.35}.ss-coming-card-body{padding:10px 12px 12px}.ss-coming-card-name{font-size:12px;font-weight:700;color:var(--morandi-text);margin-bottom:3px}.ss-coming-card-desc{font-size:10px;color:var(--morandi-text-muted);line-height:1.4;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ss-coming-badge{margin-top:6px;display:inline-block;padding:2px 8px;border-radius:999px;background:var(--morandi-purple-light);color:var(--morandi-purple);font-size:9px;font-weight:600} diff --git a/static/js/app.js b/static/js/app.js deleted file mode 100644 index ce7cc4e..0000000 --- a/static/js/app.js +++ /dev/null @@ -1,1509 +0,0 @@ -/* ========================================================= - app.js — 前端交互逻辑 v7.0 · 动态知识卡片 + 收藏历史 + 个人中心 - ========================================================= */ - -// ---- 全局状态 ---- -let currentModule = "发现"; -let currentVideoUrl = ""; -let currentVideoTitle = ""; -let currentDiscipline = "all"; -let currentSubTab = "recommend"; -let currentLifeTab = "story"; -let allVideos = []; -let allKnowledgeCards = []; -let storyData = null; -let simulatorData = null; -let currentContext = { type: "", title: "" }; -let userId = localStorage.getItem("mathAppUserId") || generateUserId(); -let userFavorites = { video: {}, knowledge: {} }; -let userAvatar = localStorage.getItem("mathAppAvatar") || "葫"; -let userAvatarColor = localStorage.getItem("mathAppAvatarColor") || "#A0B9D8"; - -let vnState = { - currentNode: null, - stats: { shuli: 10, xiayi: 10, xinzhi: 10 }, - favorability: { "欧阳侠": 0, "Eddie侠": 0, "厚朴侠": 0, "三力思侠": 0, "溜溜侠": 0, "孔锝侠": 0, "星侠": 0, "甲亢侠": 0 } -}; - -let simState = { - currentNode: null, - stats: { gpa: 10, interest: 10, skill: 10 }, - history: [] -}; - -const SIM_STAT_LABELS = { gpa: "绩点", interest: "兴趣", skill: "技能" }; -const SIM_STAT_COLORS = { gpa: "#A0B9D8", interest: "#DAB8B0", skill: "#B8D0C9" }; -const SIM_STAT_EMOJIS = { gpa: "📊", interest: "❤️", skill: "🔧" }; - -function generateUserId() { - var id = "user_" + Date.now().toString(36) + "_" + Math.random().toString(36).substr(2, 6); - localStorage.setItem("mathAppUserId", id); - return id; -} - -const STAT_LABELS = { shuli: "数理根基", xiayi: "侠义声望", xinzhi: "心志韧性" }; -const STAT_COLORS = { shuli: "#A0B9D8", xiayi: "#DAB8B0", xinzhi: "#B8D0C9" }; -const STAT_EMOJIS = { shuli: "📐", xiayi: "⚔️", xinzhi: "🧘" }; - -// 章节检测:根据节点ID前缀判断当前章节 -function detectChapter(nodeId) { - if (!nodeId) return 1; - var m = nodeId.match(/^ch(\d+)/); - return m ? parseInt(m[1]) : 1; -} - -// ---- 模块配色 ---- -const MODULE_COLORS = { - "发现": { color: "#A0B9D8", gradient: "linear-gradient(135deg, #A0B9D8, #8AA3C2)", lightBg: "#DCE6F0", name: "发现 · 值得一看的数学内容" }, - "知识": { color: "#B8D0C9", gradient: "linear-gradient(135deg, #B8D0C9, #A0BBAF)", lightBg: "#E4EFEB", name: "知识 · 数学发现" }, - "数学人生": { color: "#DAB8B0", gradient: "linear-gradient(135deg, #DAB8B0, #C9A39A)", lightBg: "#F2E4DF", name: "数学人生 · 大学选择模拟" }, - "我的": { color: "#C5CAD0", gradient: "linear-gradient(135deg, #C5CAD0, #AEB3B9)", lightBg: "#EDEEF0", name: "我的 · 个人中心" } -}; - -const MODULE_BANNERS = { - "发现": { icon: "◎", title: "发现值得一看的数学内容", sub: "从 12 个专业分区进入,直接查看该方向的视频内容" }, - "知识": { icon: "≡", title: "用数学视角理解世界", sub: "把复杂概念变成轻量、好懂、适合反复刷的知识卡片" }, - "数学人生": { icon: "◇", title: "葫芦侠行 · 互动剧情", sub: "八章完整剧情 | 四属性系统 | 多结局 | 北宋数学武侠世界" }, - "我的": { icon: "✦", title: "你的学习空间", sub: "查看资料、收藏、记录和剧情存档" } -}; - -const MODULE_CSS_CLASS = { - "发现": "shuren", "知识": "shushuo", "数学人生": "shusi", "我的": "shuwu" -}; - -const DISCIPLINE_ICONS = { - "计算机": { icon: "💻", symbol: "⌨", color: "#fff", bg: "#8BA4C0" }, - "机械工程": { icon: "⚙️", symbol: "⚙", color: "#fff", bg: "#B8A99A" }, - "电子信息": { icon: "📡", symbol: "⚡", color: "#fff", bg: "#9BB8AE" }, - "经济管理": { icon: "📊", symbol: "📈", color: "#fff", bg: "#C5A8A0" }, - "生物医学": { icon: "🧬", symbol: "❤", color: "#fff", bg: "#C4B0B8" }, - "化工材料": { icon: "🧪", symbol: "⚗", color: "#fff", bg: "#ACAEC5" }, - "人工智能": { icon: "🤖", symbol: "AI", color: "#fff", bg: "#95ABB8" }, - "微电子": { icon: "🔌", symbol: "μ", color: "#fff", bg: "#8BA4C0" }, - "自动化": { icon: "🦾", symbol: "⚙", color: "#fff", bg: "#BCA898" }, - "能源动力": { icon: "🔥", symbol: "⚡", color: "#fff", bg: "#A3B4A5" }, - "环境工程": { icon: "🌱", symbol: "✿", color: "#fff", bg: "#A8B8A8" }, - "数学建模": { icon: "📐", symbol: "Σ", color: "#fff", bg: "#9CAEC4" }, - "通用": { icon: "📚", symbol: "◎", color: "#fff", bg: "#ADB5BD" } -}; - -const AVATAR_COLORS = ["#A0B9D8","#B8D0C9","#DAB8B0","#C5CAD0","#B8A99A","#9BB8AE","#C4B0B8","#ACAEC5"]; - -// ---- 剧本广场定义 ---- -const SCRIPTS = [ - { - id: "huluxiaxing", - name: "葫芦侠行", - icon: "📖", - desc: "北宋年间,紫金山下秉公习武堂。你扮演葫芦侠,用数学武学闯荡江湖。", - meta: "93节点 · 8章 · 8+结局", - status: "ready", - cssClass: "ss-card-wuxia", - engine: "story" - }, - { - id: "simulator", - name: "数学专业模拟器", - icon: "🎓", - desc: "跑完四年大学数学专业的全流程。选课、考试、分流、毕业——你的每个选择决定结局。", - meta: "8学期 · 大三专业分流 · 多结局", - status: "beta", - cssClass: "ss-card-simulator", - engine: "simulator" - }, - { - id: "mathbti", - name: "数学人格测试", - icon: "🧮", - desc: "12道场景题,发现你的数学人格类型——你是「梦境探索者」还是「博弈游侠」?", - meta: "12题 · 4轴测评 · 12人格", - status: "ready", - cssClass: "ss-card-mathbti", - engine: "mathbti" - } -]; - -const COMING_SOON = [ - { name: "数学侦探录", icon: "🔍", desc: "民国上海,用数学破案", color: "#B8A99A" }, - { name: "星际数探", icon: "🔭", desc: "22世纪太空舰队首席数理官", color: "#9BB8AE" }, - { name: "算学千年", icon: "📜", desc: "穿越古代,亲历数学史", color: "#ACAEC5" }, - { name: "数学创业记", icon: "🚀", desc: "硅谷数学PhD辍学创业", color: "#C4B0B8" } -]; - -// ---- 角色立绘映射 (P0-2) ---- -const SPRITE_MAP = { - "葫芦侠": "https://coresg-normal.trae.ai/api/ide/v1/text_to_image?prompt=young%20Chinese%20Song%20dynasty%20scholar%20with%20a%20gourd%20hanging%20at%20his%20waist%2C%20simple%20blue-gray%20robes%2C%20gentle%20expression%2C%20clean%20ink%20wash%20painting%20style%20on%20soft%20background%2C%20morandi%20muted%20colors&image_size=portrait_4_3", - "欧阳侠": "https://coresg-normal.trae.ai/api/ide/v1/text_to_image?prompt=diligent%20young%20Chinese%20scholar%20reading%20a%20book%20at%20dawn%2C%20warm%20candle%20light%2C%20ink%20wash%20style%2C%20wearing%20simple%20robes%2C%20soft%20morandi%20beige%20and%20blue%20tones&image_size=portrait_4_3", - "溜溜侠": "https://coresg-normal.trae.ai/api/ide/v1/text_to_image?prompt=mysterious%20young%20Chinese%20female%20warrior%20with%20a%20large%20black%20dog%2C%20veiled%20face%2C%20dark%20grey%20robes%2C%20ink%20wash%20painting%20style%2C%20morandi%20muted%20dark%20tones%2C%20cool%20atmosphere&image_size=portrait_4_3", - "孔锝侠": "https://coresg-normal.trae.ai/api/ide/v1/text_to_image?prompt=strict%20Chinese%20martial%20arts%20instructor%2C%20tall%20and%20upright%2C%20stern%20but%20kind%20eyes%2C%20ink%20wash%20style%2C%20wearing%20neat%20grey-blue%20robes%2C%20morandi%20muted%20tones&image_size=portrait_4_3", - "Eddie侠": "https://coresg-normal.trae.ai/api/ide/v1/text_to_image?prompt=young%20Chinese%20scholar%20with%20copper%20coin%20spinning%20on%20finger%2C%20playful%20expression%2C%20ink%20wash%20style%2C%20soft%20morandi%20tones&image_size=portrait_4_3", - "厚朴侠": "https://coresg-normal.trae.ai/api/ide/v1/text_to_image?prompt=elegant%20Chinese%20theater%20actor%2C%20dramatic%20pose%2C%20traditional%20costume%2C%20ink%20wash%20style%2C%20morandi%20muted%20warm%20tones&image_size=portrait_4_3", - "星侠": "https://coresg-normal.trae.ai/api/ide/v1/text_to_image?prompt=gentle%20Chinese%20female%20scholar%20with%20warm%20smile%2C%20ink%20wash%20style%2C%20flowing%20robes%2C%20morandi%20soft%20pink%20and%20grey%20tones&image_size=portrait_4_3", - "甲亢侠": "https://coresg-normal.trae.ai/api/ide/v1/text_to_image?prompt=passionate%20young%20Chinese%20debater%2C%20animated%20gestures%2C%20ink%20wash%20style%2C%20morandi%20muted%20tones%2C%20intense%20eyes&image_size=portrait_4_3" -}; - -// ---- 场景背景映射 (P0-2) ---- -const BG_MAP = { - "紫金山": "https://coresg-normal.trae.ai/api/ide/v1/text_to_image?prompt=ancient%20Chinese%20academy%20on%20misty%20Purple%20Mountain%2C%20traditional%20architecture%20with%20courtyards%2C%20morning%20fog%2C%20ink%20wash%20painting%20style%2C%20morandi%20muted%20greens%20and%20greys&image_size=landscape_4_3", - "石室": "https://coresg-normal.trae.ai/api/ide/v1/text_to_image?prompt=underground%20stone%20chamber%20turned%20study%20room%2C%20candle%20lit%2C%20wooden%20shelves%20with%20scrolls%2C%20murals%20on%20rock%20walls%2C%20warm%20atmosphere%2C%20ink%20wash%20style%2C%20morandi%20warm%20greys&image_size=landscape_4_3", - "姑苏": "https://coresg-normal.trae.ai/api/ide/v1/text_to_image?prompt=Suzhou%20ancient%20canal%20town%20in%20spring%2C%20stone%20bridges%2C%20willow%20trees%2C%20traditional%20white%20wall%20buildings%2C%20gentle%20rain%2C%20ink%20wash%20painting%20style%2C%20morandi%20muted%20blue-grey%20tones&image_size=landscape_4_3", - "京城": "https://coresg-normal.trae.ai/api/ide/v1/text_to_image?prompt=ancient%20Chinese%20capital%20city%2C%20grand%20government%20hall%2C%20solemn%20atmosphere%2C%20ink%20wash%20style%2C%20morandi%20muted%20grey%20tones&image_size=landscape_4_3" -}; - -// 章节 → 默认背景映射 -const CHAPTER_BG = { - 1: "紫金山", - 2: "紫金山", - 3: "紫金山", - 4: "石室", - 5: "紫金山", - 6: "石室", - 7: "石室", - 8: "京城" -}; - -// ---- 页面初始化 ---- -document.addEventListener("DOMContentLoaded", async function () { - await loadStoryData(); - await loadUserFavorites(); - loadVNSave(); - switchModule("发现"); -}); - -// ============================================================ -// 模块切换 -// ============================================================ -function switchModule(module) { - currentModule = module; - currentDiscipline = "all"; - currentSubTab = "recommend"; - - var colors = MODULE_COLORS[module] || MODULE_COLORS["发现"]; - document.documentElement.style.setProperty("--accent", colors.color); - document.documentElement.style.setProperty("--accent-light", colors.lightBg); - document.documentElement.style.setProperty("--accent-gradient", colors.gradient); - - // 顶部品牌栏头像同步 - var brandIcon = document.querySelector(".brand-icon-sm"); - if (brandIcon) { brandIcon.textContent = "葫"; brandIcon.style.background = userAvatarColor; } - - // TabBar 高亮 - document.querySelectorAll(".tab-btn").forEach(function (btn) { - btn.classList.toggle("active", btn.dataset.module === module); - }); - - // 先隐藏所有特殊面板 - hideAllPanels(); - - if (module === "发现") { - show("searchHeader"); - show("subNav"); - show("videoList"); - // 恢复子导航默认选中"推荐" - document.querySelectorAll(".sub-nav-btn").forEach(function (btn) { - btn.classList.toggle("active", btn.dataset.tab === "recommend"); - }); - currentSubTab = "recommend"; - loadVideos("数人"); - return; - } else if (module === "知识") { - show("sectionTitleBar"); - show("shushuoHero"); - show("shushuoTopics"); - show("shushuoFeed"); - document.getElementById("contentTitle").textContent = colors.name; - loadKnowledgeCards(); - return; - } else if (module === "数学人生") { - show("scriptSquare"); - renderScriptSquare(); - return; - } else if (module === "我的") { - show("sectionTitleBar"); - show("profilePage"); - document.getElementById("contentTitle").textContent = colors.name; - loadProfileData(); - return; - } -} - -function hideAllPanels() { - hide("searchHeader"); hide("subNav"); hide("sectionTitleBar"); hide("lifeSubNav"); - hide("moduleBanner"); hide("shushuoHero"); hide("shushuoTopics"); - hide("shushuoFeed"); hide("storyContainer"); hide("simulatorContainer"); - hide("disciplineGridContainer"); hide("disciplineActiveHeader"); - hide("disciplineGrid"); hide("categoryFilter"); - hide("profilePage"); hide("scriptSquare"); - hide("loadingArea"); hide("videoList"); -} - -function show(id) { var el = document.getElementById(id); if (el) el.style.display = (id === "videoList" ? "grid" : id === "subNav" ? "flex" : "block"); } -function hide(id) { var el = document.getElementById(id); if (el) el.style.display = "none"; } - -function updateBanner(module) { - var info = MODULE_BANNERS[module] || MODULE_BANNERS["发现"]; - var el = document.getElementById("moduleBanner"); - if (!el) return; - el.style.display = "flex"; - el.className = "module-banner " + (MODULE_CSS_CLASS[module] || "shuren"); - el.innerHTML = ''; -} - -// ============================================================ -// 数人:搜索、子导航、学科网格 -// ============================================================ -function handleSearch() { - var q = document.getElementById("videoSearch").value.toLowerCase().trim(); - if (!q) { renderVideoList(allVideos); return; } - renderVideoList(allVideos.filter(function (v) { - return (v.title && v.title.toLowerCase().indexOf(q) >= 0) || - (v.description && v.description.toLowerCase().indexOf(q) >= 0) || - (v.discipline && v.discipline.toLowerCase().indexOf(q) >= 0) || - (v.author && v.author.toLowerCase().indexOf(q) >= 0); - })); -} - -function switchSubTab(tab) { - currentSubTab = tab; - document.querySelectorAll(".sub-nav-btn").forEach(function (btn) { btn.classList.toggle("active", btn.dataset.tab === tab); }); - - // 先把所有内容区重置 - var gridCtn = document.getElementById("disciplineGridContainer"); - var videoList = document.getElementById("videoList"); - var activeHdr = document.getElementById("disciplineActiveHeader"); - var catFilter = document.getElementById("categoryFilter"); - - if (gridCtn) gridCtn.style.display = "none"; - if (activeHdr) activeHdr.style.display = "none"; - if (catFilter) catFilter.style.display = "none"; - - if (tab === "discipline") { - // 专业分区:显示图标网格 - if (videoList) videoList.style.display = "none"; - renderDisciplineGrid(); - } else if (tab === "hot") { - if (videoList) videoList.style.display = "grid"; - var sorted = [].concat(allVideos).sort(function (a, b) { return (hashStr(b.title) % 1000) - (hashStr(a.title) % 1000); }); - renderVideoList(sorted); - } else { - // 推荐 - if (videoList) videoList.style.display = "grid"; - renderVideoList(allVideos); - } -} - -function renderDisciplineGrid() { - var gridCtn = document.getElementById("disciplineGridContainer"); - var grid = document.getElementById("disciplineGrid"); - var videoList = document.getElementById("videoList"); - - if (videoList) videoList.style.display = "none"; - - var keys = Object.keys(DISCIPLINE_ICONS).filter(function (d) { return d !== "通用"; }); - - grid.innerHTML = keys.map(function (d) { - var info = DISCIPLINE_ICONS[d]; - return '
' - + '
' + (info.symbol || info.icon) + '
' - + '
' + esc(d) + '
'; - }).join(""); - - // 直接设置 display,绕过 show/hide - gridCtn.style.display = "block"; - grid.style.display = "grid"; -} - -function selectDiscipline(d) { - document.getElementById("disciplineGridContainer").style.display = "none"; - document.getElementById("categoryFilter").style.display = "none"; - document.getElementById("videoList").style.display = "grid"; - document.getElementById("disciplineActiveHeader").style.display = "flex"; - document.getElementById("activeDisciplineName").textContent = d; - renderVideoList(allVideos.filter(function (v) { return v.discipline === d; })); -} - -function backToDisciplineGrid() { - document.getElementById("disciplineActiveHeader").style.display = "none"; - document.getElementById("categoryFilter").style.display = "none"; - renderDisciplineGrid(); -} - -// ============================================================ -// 数说:发现Feed(动态版) -// ============================================================ -async function loadKnowledgeCards(topic) { - show("loadingArea"); - hide("shushuoFeed"); - var url = "/api/knowledge"; - if (topic) url += "?topic=" + encodeURIComponent(topic); - try { - var resp = await fetch(url); - var result = await resp.json(); - hide("loadingArea"); - if (result.code === 0) { - allKnowledgeCards = result.data || []; - var topics = result.topics || []; - renderShushuoTopics(topics); - renderShushuoFeed(allKnowledgeCards); - } - } catch (e) { - hide("loadingArea"); - } -} - -function renderShushuoTopics(topics) { - var scrollEl = document.getElementById("topicsScroll"); - scrollEl.innerHTML = ('全部') + - topics.map(function (t) { - return '' + esc(t) + ''; - }).join(""); -} - -function renderShushuoFeed(cards) { - show("shushuoFeed"); - updateShushuoHero(); - var feed = document.getElementById("shushuoFeed"); - feed.innerHTML = cards.map(function (c) { - var isFav = userFavorites.knowledge[c.id] || false; - return '
' - + '
' - + '
' + (c.icon || "📖") + '
' - + '
' + esc(c.question || "") + '
' - + '' - + '
' - + '
' + esc(c.title) + '
' - + '
' + esc(c.description) + '
' - + '
'; - }).join(""); -} - -function filterShushuoByTopic(topic) { - document.querySelectorAll(".topic-pill").forEach(function (p) { - var is = p.textContent.trim() === (topic || "全部"); - p.classList.toggle("active", is); - }); - if (topic) { - loadKnowledgeCards(topic); - } else { - loadKnowledgeCards(); - } - document.querySelector(".content-area").scrollTo({ top: 0, behavior: "smooth" }); -} - -function openKnowledgeDetail(cardId) { - var card = allKnowledgeCards.find(function (c) { return c.id === cardId; }); - if (!card) return; - setAIContext("knowledge", card.title); - recordHistory("knowledge", card.id, card.title, card.icon || "📖"); - // 在聊天中询问 - openChat(); - quickAsk(card.title + "是什么?能给我详细讲讲吗?"); -} - -async function toggleFavorite(type, targetId, title, cover) { - var isFav = (userFavorites[type] || {})[targetId]; - if (isFav) { - // 取消收藏 - try { - await fetch("/api/favorites", { - method: "DELETE", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ user_id: userId, target_type: type, target_id: targetId }) - }); - if (userFavorites[type]) delete userFavorites[type][targetId]; - showToast("已取消收藏"); - } catch (e) { /* ignore */ } - } else { - // 添加收藏 - try { - await fetch("/api/favorites", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ user_id: userId, target_type: type, target_id: targetId, title: title, cover: cover }) - }); - if (!userFavorites[type]) userFavorites[type] = {}; - userFavorites[type][targetId] = true; - showToast("已加入收藏"); - } catch (e) { /* ignore */ } - } - // 刷新当前视图 - if (currentModule === "知识" && type === "knowledge") { - renderShushuoFeed(allKnowledgeCards); - } else if (currentModule === "发现" && type === "video") { - renderVideoList(allVideos); - } -} - -async function loadUserFavorites() { - try { - var resp = await fetch("/api/favorites?user_id=" + userId); - var result = await resp.json(); - if (result.code === 0) { - userFavorites = { video: {}, knowledge: {} }; - (result.data || []).forEach(function (f) { - if (!userFavorites[f.target_type]) userFavorites[f.target_type] = {}; - userFavorites[f.target_type][f.target_id] = true; - }); - } - } catch (e) { userFavorites = { video: {}, knowledge: {} }; } -} - -async function recordHistory(type, targetId, title, cover) { - try { - await fetch("/api/history", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ user_id: userId, target_type: type, target_id: targetId, title: title, cover: cover }) - }); - } catch (e) { /* ignore */ } -} - -function showToast(message) { - var toast = document.getElementById("globalToast"); - if (!toast) { - toast = document.createElement("div"); - toast.id = "globalToast"; - toast.style.cssText = "position:fixed;bottom:100px;left:50%;transform:translateX(-50%);background:rgba(0,0,0,0.78);color:#fff;padding:8px 20px;border-radius:20px;font-size:13px;z-index:9999;pointer-events:none;transition:opacity 0.3s;opacity:0;"; - document.body.appendChild(toast); - } - toast.textContent = message; - toast.style.opacity = "1"; - clearTimeout(toast._timer); - toast._timer = setTimeout(function () { toast.style.opacity = "0"; }, 1500); -} - -// ============================================================ -// 个人中心 -// ============================================================ -async function loadProfileData() { - try { - var [statsResp, profileResp] = await Promise.all([ - fetch("/api/user/stats?user_id=" + userId), - fetch("/api/user/profile?user_id=" + userId) - ]); - var stats = (await statsResp.json()).data || { watch_count: 0, favorite_count: 0 }; - var profile = (await profileResp.json()).data || {}; - - // 更新统计数据 - var statNums = document.querySelectorAll(".profile-stat-num"); - if (statNums.length >= 3) { - statNums[0].textContent = stats.watch_count || 0; - statNums[1].textContent = stats.favorite_count || 0; - statNums[2].textContent = "2"; // 剧情存档数保持固定 - } - // 更新头像和昵称 - var avatar = document.getElementById("profileAvatar"); - if (avatar) { - avatar.textContent = (profile.avatar_emoji || userAvatar); - avatar.style.background = (profile.avatar_color || userAvatarColor); - userAvatar = profile.avatar_emoji || userAvatar; - userAvatarColor = profile.avatar_color || userAvatarColor; - } - - var nameEl = document.getElementById("profileName"); - if (nameEl) nameEl.textContent = (profile.nickname || "游客用户"); - - var idEl = document.getElementById("profileId"); - if (idEl) idEl.textContent = "ID: " + userId.substring(0, 14); - } catch (e) { /* ignore */ } -} - -function showFavorites() { - loadFavoritesList(); -} - -async function loadFavoritesList() { - showProfileListPanel("我的收藏"); - var panel = document.getElementById("profileListContent"); - panel.innerHTML = '
'; - try { - var resp = await fetch("/api/favorites?user_id=" + userId); - var result = await resp.json(); - var items = result.data || []; - if (items.length === 0) { - panel.innerHTML = '

还没有收藏

去发现有趣的内容吧

'; - } else { - panel.innerHTML = items.map(function (f) { - return '
' - + '
' + (f.cover || "◎") + '
' - + '
' + esc(f.title) + '
' + f.target_type + ' · ' + (f.created_at || "").substring(0, 10) + '
' - + '' - + '
'; - }).join(""); - } - } catch (e) { - panel.innerHTML = '
⚠️

加载失败

'; - } -} - -function showHistory() { - loadHistoryList(); -} - -async function loadHistoryList() { - showProfileListPanel("观看记录"); - var panel = document.getElementById("profileListContent"); - panel.innerHTML = '
'; - try { - var resp = await fetch("/api/history?user_id=" + userId + "&limit=30"); - var result = await resp.json(); - var items = result.data || []; - if (items.length === 0) { - panel.innerHTML = '
👀

还没有观看记录

去看看视频和知识卡片吧

'; - } else { - panel.innerHTML = items.map(function (h) { - return '
' - + '
' + (h.cover || "◎") + '
' - + '
' + esc(h.title) + '
' + h.target_type + ' · ' + (h.watched_at || "").substring(0, 16) + '
' - + '
'; - }).join(""); - } - } catch (e) { - panel.innerHTML = '
⚠️

加载失败

'; - } -} - -function showProfileListPanel(title) { - var panel = document.getElementById("profileListPanel"); - var titleEl = document.getElementById("profileListTitle"); - if (titleEl) titleEl.textContent = title; - if (panel) panel.style.display = "block"; - // 隐藏个人中心其他内容 - var profilePage = document.getElementById("profilePage"); - if (profilePage) profilePage.style.display = "none"; -} - -function hideProfileListPanel() { - var panel = document.getElementById("profileListPanel"); - if (panel) panel.innerHTML = ""; - if (panel) panel.style.display = "none"; - var profilePage = document.getElementById("profilePage"); - if (profilePage) profilePage.style.display = "block"; - loadProfileData(); -} - -async function removeFavorite(type, targetId) { - await toggleFavorite(type, targetId, "", ""); - loadFavoritesList(); - loadProfileData(); -} - -function editNickname() { - var name = prompt("输入新昵称:", document.querySelector(".profile-name").textContent); - if (name && name.trim()) { - var trimmed = name.trim().substring(0, 12); - fetch("/api/user/profile", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ user_id: userId, nickname: trimmed }) - }).then(function () { - var el = document.querySelector(".profile-name"); - if (el) el.textContent = trimmed; - showToast("昵称已更新"); - }); - } -} -function updateShushuoHero() { - document.getElementById("heroTitle").textContent = "数学不是公式,是你理解世界的方式"; - document.getElementById("heroDesc").textContent = "每天一个数学概念,让思维变得更清晰"; - var el = document.querySelector(".hero-stats"); - if (el) el.innerHTML = "📖 " + allKnowledgeCards.length + "个概念⏱ 阅读约" + (allKnowledgeCards.length * 5) + "分钟"; -} -function adjustColor(hex, amount) { - var num = parseInt(hex.replace("#",""), 16); - var r = Math.min(255, (num >> 16) + amount); - var g = Math.min(255, ((num >> 8) & 0x00FF) + amount); - var b = Math.min(255, (num & 0x0000FF) + amount); - return "#" + ((r << 16) | (g << 8) | b).toString(16).padStart(6, "0"); -} - -// ============================================================ -// 视频加载与渲染 -// ============================================================ -async function loadVideos(module, discipline) { - show("loadingArea"); - document.getElementById("videoList").innerHTML = ""; - var url = "/api/videos?module=" + encodeURIComponent(module); - if (discipline && discipline !== "all") url += "&discipline=" + encodeURIComponent(discipline); - try { - var resp = await fetch(url); - var result = await resp.json(); - hide("loadingArea"); - allVideos = result.data || []; - if (allVideos.length > 0) { - renderVideoList(allVideos); - } else { - document.getElementById("videoList").innerHTML = '
📭

暂无相关视频

后续将由各学院志愿者上传

'; - } - } catch (e) { - hide("loadingArea"); - document.getElementById("videoList").innerHTML = '
⚠️

加载失败

请稍后重试

'; - } -} - -function renderVideoList(videos) { - var list = document.getElementById("videoList"); - if (!videos || videos.length === 0) { list.innerHTML = '
📭

暂无相关视频

'; return; } - list.style.display = "grid"; - list.innerHTML = videos.map(function (v) { - var avatarColor = AVATAR_COLORS[hashStr(v.author || "") % AVATAR_COLORS.length]; - var initial = (v.author || "?").replace(/学院|·|同学|系/g, "").slice(0, 1); - var dur = v.duration_min || 5; - var icon = v.discipline_icon && v.discipline_icon !== "·" ? v.discipline_icon : "◎"; - var coverBg = "hsl(" + (hashStr(v.discipline || "") % 360) + ", 40%, 90%)"; - var isFav = userFavorites.video[v.id] || false; - return '
' - + '
' + icon + '
' + dur + ':00
' - + '' - + '
' - + '
' + esc(v.title) + '
' - + '
' + initial + '' - + '' + esc(v.author) + '' - + '
👁️' + ((hashStr(v.title) % 1000) + 100) + '
'; - }).join(""); -} - -function openVideo(id, title, desc, url, author, cat) { - currentVideoUrl = url; - currentVideoTitle = title; - document.getElementById("videoModalTitle").textContent = title; - document.getElementById("videoModalDesc").textContent = desc; - document.getElementById("videoModalAuthor").innerHTML = author; - document.getElementById("videoModalCat").innerHTML = cat || "通用"; - document.getElementById("videoLinkBtn").style.display = url ? "block" : "none"; - document.getElementById("videoPlaceholderText").textContent = url ? "点击下方按钮查看完整课程视频" : "视频链接待补充"; - // 收藏状态 - var favBtn = document.getElementById("videoModalFav"); - var vid = parseInt(id); - favBtn.textContent = (userFavorites.video[vid]) ? "★ 已收藏" : "☆ 收藏"; - favBtn.dataset.vid = vid; - document.getElementById("videoModal").classList.add("show"); - // AI上下文 - setAIContext("video", title); - // 记录观看历史 - recordHistory("video", vid, title, cat || "◎"); -} -function closeVideo() { document.getElementById("videoModal").classList.remove("show"); } -function openVideoLink() { if (currentVideoUrl) window.open(currentVideoUrl, "_blank"); } -function toggleVideoFav() { - var btn = document.getElementById("videoModalFav"); - var vid = parseInt(btn.dataset.vid); - var title = currentVideoTitle; - toggleFavorite("video", vid, title, "◎"); - btn.textContent = (userFavorites.video[vid]) ? "★ 已收藏" : "☆ 收藏"; -} - -// ============================================================ -// 视觉小说引擎 (数思) — 唯一引擎 -// ============================================================ -async function loadStoryData() { - try { - var resp = await fetch("/api/story"); - var result = await resp.json(); - if (result.code === 0) storyData = result.data; - } catch (e) { console.error("故事数据加载失败:", e); } -} - -function loadVNSave() { - try { var s = localStorage.getItem("mathAppVNSave"); if (s) vnState = JSON.parse(s); } catch (e) {} -} - -function saveVNState() { - localStorage.setItem("mathAppVNSave", JSON.stringify(vnState)); - alert("进度已存档!"); -} - -function resetVNSave() { - var startNode = (storyData && storyData.start_node) || "ch1_start"; - vnState = { currentNode: startNode, stats: { shuli: 10, xiayi: 10, xinzhi: 10 }, favorability: { "欧阳侠": 0, "Eddie侠": 0, "厚朴侠": 0, "三力思侠": 0, "溜溜侠": 0, "孔锝侠": 0, "星侠": 0, "甲亢侠": 0 } }; - localStorage.removeItem("mathAppVNSave"); - navigateVN(startNode); -} - -function applyVNStats(effects) { - if (!effects) return; - if (effects.shuli) vnState.stats.shuli = (vnState.stats.shuli || 10) + effects.shuli; - if (effects.xiayi) vnState.stats.xiayi = (vnState.stats.xiayi || 10) + effects.xiayi; - if (effects.xinzhi) vnState.stats.xinzhi = (vnState.stats.xinzhi || 10) + effects.xinzhi; - // 兼容旧属性名 - if (effects.intelligence !== undefined) vnState.stats.shuli = (vnState.stats.shuli || 10) + effects.intelligence; - if (effects.charm !== undefined) vnState.stats.xiayi = (vnState.stats.xiayi || 10) + effects.charm; - if (effects.stress !== undefined) vnState.stats.xinzhi = (vnState.stats.xinzhi || 10) - effects.stress; - // 好感度 - if (effects.favorability) { - if (!vnState.favorability) vnState.favorability = {}; - for (var char in effects.favorability) { - vnState.favorability[char] = (vnState.favorability[char] || 0) + effects.favorability[char]; - } - } -} - -function startStoryEngine() { - setAIContext("story", "葫芦侠行"); - var startNode = (storyData && storyData.start_node) || "ch1_start"; - // 如果存档中有旧节点但新JSON里不存在,回退到起始节点 - var savedNode = vnState.currentNode; - if (savedNode && storyData && storyData.nodes && storyData.nodes[savedNode]) { - navigateVN(savedNode); - } else { - vnState.currentNode = startNode; - navigateVN(startNode); - } -} - -function navigateVN(nodeId) { - if (!storyData || !storyData.nodes || !storyData.nodes[nodeId]) { - document.getElementById("storyContainer").innerHTML = '

剧情加载失败

节点未找到

'; - return; - } - var node = storyData.nodes[nodeId]; - vnState.currentNode = nodeId; - - var container = document.getElementById("storyContainer"); - if (!container) return; - container.style.display = "block"; - container.scrollIntoView({ behavior: "smooth" }); - - // 构建完整 HTML - var ch = detectChapter(nodeId); - var chNames = ["","初入江湖","西部教习","数理论道","脑学初创","报国之路","暗流涌动","陋室修炼","真相之门"]; - var chName = chNames[ch] || ""; - - var html = ''; - html += '
'; - html += '
第' + ch + '章 · ' + chName + '
'; - html += '
'; - html += '📐 数理根基: ' + (vnState.stats.shuli || 10) + ''; - html += '⚔️ 侠义声望: ' + (vnState.stats.xiayi || 10) + ''; - html += '🧘 心志韧性: ' + (vnState.stats.xinzhi || 10) + ''; - html += '
'; - html += '
'; - html += ''; - html += ''; - html += ''; - html += '
'; - - // 场景背景 + 立绘 - var bgKey = CHAPTER_BG[ch] || "紫金山"; - var bgUrl = BG_MAP[bgKey] || ""; - if (node.background) bgUrl = BG_MAP[node.background] || node.background || bgUrl; - - // 立绘:优先 node.sprite,其次从 SPRITE_MAP 查找角色名 - var spriteUrl = ""; - var characterName = node.character || ""; - if (node.sprite) { - spriteUrl = node.sprite; - } else if (characterName) { - // 匹配 SPRITE_MAP 中的角色(支持多角色用 & 分隔) - var names = characterName.split("&"); - for (var ni = 0; ni < names.length; ni++) { - var n = names[ni].trim(); - if (SPRITE_MAP[n]) { spriteUrl = SPRITE_MAP[n]; break; } - } - } - - html += '
'; - if (bgUrl) html += ''; - if (spriteUrl) html += '' + esc(characterName) + ''; - html += '
'; - if (characterName) html += '
' + esc(characterName) + '
'; - html += '
'; - html += '
'; - - html += '
'; - - container.innerHTML = html; - - // 打字机效果 - var sceneText = document.getElementById("sceneText"); - var text = node.scene || ""; - var i = 0; - function typeNext() { - if (i < text.length) { - sceneText.textContent += text.charAt(i); - i++; - setTimeout(typeNext, 25); - } else { - buildChoiceButtons(node.choices || []); - } - } - sceneText.textContent = ""; - typeNext(); -} - -function buildChoiceButtons(choices) { - var area = document.getElementById("choicesArea"); - if (!area) return; - area.innerHTML = ""; - - choices.forEach(function (c, idx) { - var btn = document.createElement("button"); - btn.className = "choice-btn"; - btn.style.animationDelay = (idx * 0.1) + "s"; - - // 效果文本 - var effectsHTML = ""; - if (c.effects) { - var parts = []; - if (c.effects.shuli) parts.push("数理根基" + (c.effects.shuli > 0 ? "+" : "") + c.effects.shuli); - if (c.effects.xiayi) parts.push("侠义声望" + (c.effects.xiayi > 0 ? "+" : "") + c.effects.xiayi); - if (c.effects.xinzhi) parts.push("心志韧性" + (c.effects.xinzhi > 0 ? "+" : "") + c.effects.xinzhi); - // 兼容旧属性 - if (c.effects.intelligence) parts.push("数理根基" + (c.effects.intelligence > 0 ? "+" : "") + c.effects.intelligence); - if (c.effects.charm) parts.push("侠义声望" + (c.effects.charm > 0 ? "+" : "") + c.effects.charm); - if (c.effects.stress) parts.push("心志韧性" + (c.effects.stress > 0 ? "+" : "") + c.effects.stress); - if (c.effects.favorability) { - for (var char in c.effects.favorability) { - var val = c.effects.favorability[char]; - parts.push(char + "好感" + (val > 0 ? "+" : "") + val); - } - } - if (parts.length > 0) effectsHTML = '[' + parts.join(" | ") + ']'; - } - - btn.innerHTML = '' + esc(c.text) + '' + effectsHTML; - - btn.onclick = (function (nextNode, effects) { - return function () { - applyVNStats(effects); - navigateVN(nextNode); - }; - })(c.next, c.effects); - - area.appendChild(btn); - }); -} - -// ============================================================ -// AI 聊天 -// ============================================================ -function openChat() { document.getElementById("chatOverlay").classList.add("show"); document.getElementById("chatMessages").scrollTop = document.getElementById("chatMessages").scrollHeight; } -function closeChat() { document.getElementById("chatOverlay").classList.remove("show"); } -function quickAsk(q) { document.getElementById("chatInput").value = q; sendMessage(); } - -async function sendMessage() { - var input = document.getElementById("chatInput"); - var message = input.value.trim(); - if (!message) return; - appendChat("user", message); - input.value = ""; - var typingId = appendTyping(); - try { - var resp = await fetch("/api/chat", { - method: "POST", headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ message: message, context: currentContext }) - }); - var result = await resp.json(); - removeTyping(typingId); - appendChat("assistant", result.code === 0 ? result.reply : "抱歉,AI 助教暂时无法回复。"); - } catch (e) { - removeTyping(typingId); - appendChat("assistant", "网络异常,请稍后重试。"); - } -} - -function setAIContext(type, title) { - currentContext = { type: type, title: title || "" }; - var hint = document.getElementById("chatContextHint"); - if (!hint) { - hint = document.createElement("div"); - hint.id = "chatContextHint"; - hint.style.cssText = "padding:6px 12px;font-size:11px;color:var(--accent);background:var(--accent-light);text-align:center;"; - var chatPanel = document.querySelector(".chat-panel"); - var header = document.querySelector(".chat-header"); - if (header && chatPanel) chatPanel.insertBefore(hint, header.nextSibling); - } - hint.textContent = currentContext.title ? "📍 当前:" + currentContext.title : ""; - hint.style.display = currentContext.title ? "block" : "none"; -} -function appendChat(role, text) { - var container = document.getElementById("chatMessages"); - var div = document.createElement("div"); - div.className = "chat-msg " + role; - div.innerHTML = '
' + esc(text) + "
"; - container.appendChild(div); - container.scrollTop = container.scrollHeight; -} -function appendTyping() { - var container = document.getElementById("chatMessages"); - var div = document.createElement("div"); - div.className = "chat-msg assistant typing-msg"; - div.innerHTML = '
'; - container.appendChild(div); - container.scrollTop = container.scrollHeight; - return div; -} -function removeTyping(el) { if (el && el.parentNode) el.remove(); } - -// ============================================================ -// 数学人生子标签切换 -// ============================================================ -function switchLifeTab(tab) { - currentLifeTab = tab; - document.querySelectorAll(".life-sub-btn").forEach(function (b) { b.classList.toggle("active", b.dataset.life === tab); }); - if (tab === "simulator") { - hide("storyContainer"); - show("simulatorContainer"); - setAIContext("simulator", "数学专业模拟器"); - startSimulator(); - } else { - hide("simulatorContainer"); - show("storyContainer"); - setAIContext("story", "葫芦侠行"); - startStoryEngine(); - } -} - -// ============================================================ -// 数学专业模拟器引擎 -// ============================================================ -async function loadSimulatorData() { - try { - var resp = await fetch("/api/simulator"); - var result = await resp.json(); - if (result.code === 0) simulatorData = result.data; - } catch (e) { console.error("模拟器数据加载失败:", e); } -} - -async function loadSimSave() { - try { - var resp = await fetch("/api/simulator/save?user_id=" + userId); - var result = await resp.json(); - if (result.code === 0 && result.data) { - simState = result.data; - } - } catch (e) {} -} - -function saveSimState() { - simState.history.push(simState.currentNode); - fetch("/api/simulator/save", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ user_id: userId, state: simState }) - }).catch(function () {}); -} - -function resetSimulator() { - simState = { currentNode: (simulatorData && simulatorData.start_node) || "y1_welcome", stats: { gpa: 10, interest: 10, skill: 10 }, history: [] }; - saveSimState(); - navigateSimulator(simState.currentNode); -} - -async function startSimulator() { - if (!simulatorData) await loadSimulatorData(); - await loadSimSave(); - if (!simState.currentNode || !simulatorData || !simulatorData.nodes || !simulatorData.nodes[simState.currentNode]) { - resetSimulator(); - } else { - navigateSimulator(simState.currentNode); - } -} - -function navigateSimulator(nodeId) { - if (!simulatorData || !simulatorData.nodes || !simulatorData.nodes[nodeId]) { - document.getElementById("simulatorContainer").innerHTML = '

剧情加载失败

'; - return; - } - var node = simulatorData.nodes[nodeId]; - simState.currentNode = nodeId; - saveSimState(); - - var container = document.getElementById("simulatorContainer"); - if (!container) return; - container.style.display = "block"; - - // 如果遇到结局节点 - if (node.end) { - showEnding(node); - return; - } - - container.scrollIntoView({ behavior: "smooth" }); - var ch = (node.year || 1); - var stage = node.stage || ""; - - var html = ''; - html += '
'; - html += '
🎓 大' + ["", "一", "二", "三", "四"][ch] + ' · ' + stage + '
'; - html += '
'; - Object.keys(simState.stats).forEach(function (k) { - html += '' + SIM_STAT_EMOJIS[k] + ' ' + SIM_STAT_LABELS[k] + ': ' + simState.stats[k] + ''; - }); - html += '
'; - html += '
'; - html += ''; - html += ''; - html += ''; - html += ''; - html += '
'; - - html += '
'; - if (node.character) html += '
' + esc(node.character) + '
'; - html += '
'; - html += '
'; - - html += '
'; - - container.innerHTML = html; - - // 打字机效果 - var textEl = document.getElementById("simText"); - var text = node.scene || ""; - var i = 0; - function typeNext() { - if (i < text.length) { - textEl.textContent += text.charAt(i); - i++; - setTimeout(typeNext, 20); - } else { - buildSimChoices(node.choices || []); - } - } - textEl.textContent = ""; - typeNext(); -} - -function buildSimChoices(choices) { - var area = document.getElementById("simChoicesArea"); - if (!area) return; - area.innerHTML = ""; - - choices.forEach(function (c, idx) { - var btn = document.createElement("button"); - btn.className = "sim-choice-btn"; - btn.style.animationDelay = (idx * 0.1) + "s"; - - var effectsHTML = ""; - if (c.effects) { - var parts = []; - if (c.effects.gpa) parts.push((c.effects.gpa > 0 ? "+" : "") + c.effects.gpa + " 绩点"); - if (c.effects.interest) parts.push((c.effects.interest > 0 ? "+" : "") + c.effects.interest + " 兴趣"); - if (c.effects.skill) parts.push((c.effects.skill > 0 ? "+" : "") + c.effects.skill + " 技能"); - if (parts.length > 0) effectsHTML = '[' + parts.join(" | ") + ']'; - } - if (c.hint) effectsHTML += '💡 ' + esc(c.hint) + ''; - - btn.innerHTML = '' + esc(c.text) + '' + effectsHTML; - - btn.onclick = (function (nextNode, effects) { - return function () { - if (effects) applySimStats(effects); - navigateSimulator(nextNode); - }; - })(c.next, c.effects); - - area.appendChild(btn); - }); -} - -function applySimStats(effects) { - if (!effects) return; - if (effects.gpa) simState.stats.gpa = Math.max(0, Math.min(30, (simState.stats.gpa || 10) + effects.gpa)); - if (effects.interest) simState.stats.interest = Math.max(0, Math.min(30, (simState.stats.interest || 10) + effects.interest)); - if (effects.skill) simState.stats.skill = Math.max(0, Math.min(30, (simState.stats.skill || 10) + effects.skill)); -} - -function showEnding(node) { - document.getElementById("endingOverlay").classList.add("show"); - var badges = { pure_math: "🔮", ai: "🤖", finance: "💰", research: "🔬", startup: "🚀", teacher: "📚", engineer: "⚙️", security: "🔒" }; - document.getElementById("endingBadge").textContent = badges[node.ending_type] || "🎓"; - document.getElementById("endingType").textContent = node.character || "结局"; - document.getElementById("endingScene").textContent = node.scene || ""; - document.getElementById("endingTitle").textContent = node.stage || ""; - - // 记录解锁结局 - if (node.ending_type) unlockEnding(node.ending_type); - - // 统计数据 - var statsHtml = '
'; - Object.keys(simState.stats).forEach(function (k) { - statsHtml += '
' + simState.stats[k] + '
' + SIM_STAT_LABELS[k] + '
'; - }); - statsHtml += '
'; - document.getElementById("endingStats").innerHTML = statsHtml; -} - -function closeEnding() { document.getElementById("endingOverlay").classList.remove("show"); } - -// ============================================================ -// 头像选择器 -// ============================================================ -var PICKER_EMOJIS = ["π","∑","∫","∞","Δ","∂","α","β","γ","λ","μ","σ","Ω","∇","⊥","⊕","⊗","≈","±","÷","√","∏","∃","∀","∈","⊂","∪","∩","ℵ","∮"]; - -function openAvatarPicker() { - document.getElementById("avatarPickerOverlay").classList.add("show"); - // 渲染emoji网格 - var grid = document.getElementById("pickerEmojiGrid"); - grid.innerHTML = PICKER_EMOJIS.map(function (e) { - var selected = e === userAvatar ? ' style="border-color:var(--accent);background:var(--accent-light)"' : ""; - return ''; - }).join(""); - // 渲染颜色行 - var colors = ["#A0B9D8","#B8D0C9","#DAB8B0","#C5CAD0","#B8A99A","#9BB8AE","#ACAEC5","#C4B0B8"]; - var colorRow = document.getElementById("pickerColorRow"); - colorRow.innerHTML = colors.map(function (c) { - var selected = c === userAvatarColor ? ' style="box-shadow:0 0 0 4px ' + c + '44"' : ""; - return ''; - }).join(""); -} - -function selectAvatar(emoji) { - userAvatar = emoji; - localStorage.setItem("mathAppAvatar", emoji); - openAvatarPicker(); // refresh grid - updateAvatarUI(); - fetch("/api/user/profile", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ user_id: userId, avatar_emoji: emoji }) - }); -} - -function selectAvatarColor(color) { - userAvatarColor = color; - localStorage.setItem("mathAppAvatarColor", color); - openAvatarPicker(); - updateAvatarUI(); - fetch("/api/user/profile", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ user_id: userId, avatar_color: color }) - }); -} - -function updateAvatarUI() { - var av = document.getElementById("profileAvatar"); - if (av) { av.textContent = userAvatar; av.style.background = userAvatarColor; } - var brandIcon = document.querySelector(".brand-icon-sm"); - if (brandIcon) { brandIcon.textContent = "葫"; brandIcon.style.background = userAvatarColor; } -} - -function closeAvatarPicker(e) { - if (e && e.target !== document.getElementById("avatarPickerOverlay")) return; - document.getElementById("avatarPickerOverlay").classList.remove("show"); -} - -// ---- 工具函数 ---- -function esc(text) { - if (!text) return ""; - var div = document.createElement("div"); - div.textContent = text; - return div.innerHTML; -} -function hashStr(str) { - var hash = 0; - for (var i = 0; i < str.length; i++) { hash = ((hash << 5) - hash) + str.charCodeAt(i); hash |= 0; } - return Math.abs(hash); -} - -// ============================================================ -// 剧本广场 (Script Square) -// ============================================================ -function renderScriptSquare() { - try { - // 渲染精选剧本大卡 - var heroCards = document.getElementById("ssHeroCards"); - if (!heroCards) return; - heroCards.innerHTML = SCRIPTS.map(function (s) { - var statusLabel = s.status === "beta" ? "开发中 · 抢先体验" : "已上线"; - return '
' - + '
' - + '
' - + '
' + s.icon + '
' - + '
' + esc(s.name) + '
' - + '
' + esc(s.desc) + '
' - + '
' + s.meta + '' + statusLabel + '
' - + '
'; - }).join(""); - - // 渲染即将上线小卡 - var comingCards = document.getElementById("ssComingCards"); - if (comingCards) { - comingCards.innerHTML = COMING_SOON.map(function (c) { - return '
' - + '
' + c.icon + '
' - + '
' - + '
' + esc(c.name) + '
' - + '
' + esc(c.desc) + '
' - + '敬请期待' - + '
'; - }).join(""); - } - - // 检查是否有可继续的存档 - updateContinueCard(); - } catch (e) { - console.error("剧本广场渲染失败:", e); - } -} - -function updateContinueCard() { - try { - var continueEl = document.getElementById("ssContinue"); - if (!continueEl) return; - var vnSavedNode = vnState.currentNode; - var simSavedNode = simState.currentNode; - - // 优先显示葫芦侠行存档,其次模拟器存档 - if (vnSavedNode && storyData && storyData.nodes && storyData.nodes[vnSavedNode]) { - var ch = detectChapter(vnSavedNode); - var chNames = ["","初入江湖","西部教习","数理论道","脑学初创","报国之路","暗流涌动","陋室修炼","真相之门"]; - document.getElementById("ssContinueTitle").textContent = "葫芦侠行 · 第" + ch + "章"; - document.getElementById("ssContinueSub").textContent = chNames[ch] || ""; - document.getElementById("ssContinueCard").onclick = function () { enterScript("huluxiaxing"); }; - continueEl.style.display = "block"; - } else if (simSavedNode && simulatorData && simulatorData.nodes && simulatorData.nodes[simSavedNode]) { - var node = simulatorData.nodes[simSavedNode]; - document.getElementById("ssContinueTitle").textContent = "数学专业模拟器"; - document.getElementById("ssContinueSub").textContent = (node.stage || "") + (node.year ? " · 大" + ["","一","二","三","四"][node.year] : ""); - document.getElementById("ssContinueCard").onclick = function () { enterScript("simulator"); }; - continueEl.style.display = "block"; - } else { - continueEl.style.display = "none"; - } - } catch (e) { - console.error("继续卡片更新失败:", e); - } -} - -function enterScript(scriptId) { - hide("scriptSquare"); - - if (scriptId === "huluxiaxing") { - setAIContext("story", "葫芦侠行"); - hide("simulatorContainer"); - show("lifeSubNav"); - show("storyContainer"); - var btns = document.querySelectorAll(".life-sub-btn"); - btns.forEach(function (b) { - b.classList.toggle("active", b.dataset.life === "story"); - }); - startStoryEngine(); - } else if (scriptId === "simulator") { - setAIContext("simulator", "数学专业模拟器"); - hide("storyContainer"); - show("lifeSubNav"); - show("simulatorContainer"); - var btns = document.querySelectorAll(".life-sub-btn"); - btns.forEach(function (b) { - b.classList.toggle("active", b.dataset.life === "simulator"); - }); - startSimulator(); - } else if (scriptId === "mathbti") { - // 打开 MathBTI 独立页面 - window.location.href = "/mathbti"; - } -} - -function backToSquare() { - hide("lifeSubNav"); - hide("storyContainer"); - hide("simulatorContainer"); - show("scriptSquare"); - renderScriptSquare(); - document.querySelector(".content-area").scrollTo({ top: 0, behavior: "smooth" }); -} - -// ============================================================ -// 存档系统 (Save System — v8.3) -// ============================================================ -function getSaveKey(scriptId) { return "mathAppSave_" + scriptId; } - -function loadAllSaves(scriptId) { - try { - var raw = localStorage.getItem(getSaveKey(scriptId)); - return raw ? JSON.parse(raw) : []; - } catch (e) { return []; } -} - -function saveAllSaves(scriptId, saves) { - localStorage.setItem(getSaveKey(scriptId), JSON.stringify(saves)); -} - -function openSavePanel() { - var scriptId = (currentLifeTab === "simulator") ? "simulator" : "huluxiaxing"; - document.getElementById("savePanelOverlay").classList.add("show"); - renderSaveSlots(scriptId); -} - -function closeSavePanel() { - document.getElementById("savePanelOverlay").classList.remove("show"); -} - -function renderSaveSlots(scriptId) { - var slots = document.getElementById("saveSlots"); - var saves = loadAllSaves(scriptId); - var MAX_SLOTS = 5; - - var html = ""; - for (var i = 0; i < MAX_SLOTS; i++) { - var s = saves[i]; - if (s) { - // 已使用的槽位 - var icon = scriptId === "simulator" ? "🎓" : "📖"; - var name = (scriptId === "simulator") ? "数学专业模拟器" : "葫芦侠行"; - var meta = s.chapter || ""; - var date = s.savedAt ? s.savedAt.substring(0, 16).replace("T", " ") : ""; - html += '
'; - html += '
' + icon + '
'; - html += '
'; - html += '
' + esc(name) + '
'; - html += '
' + esc(meta) + '' + date + '
'; - html += '
'; - html += '
'; - html += ''; - html += ''; - html += '
'; - } else { - // 空槽位 - html += '
'; - html += '+ 空槽位 ' + (i + 1) + ' — 点击保存'; - html += '
'; - } - } - slots.innerHTML = html; -} - -function saveToSlot(scriptId, slotIndex) { - var saveData = { - savedAt: new Date().toISOString(), - scriptId: scriptId - }; - - if (scriptId === "huluxiaxing") { - saveData.chapter = detectChapter(vnState.currentNode) ? "第" + detectChapter(vnState.currentNode) + "章" : ""; - saveData.nodeId = vnState.currentNode; - saveData.stats = JSON.parse(JSON.stringify(vnState.stats)); - saveData.favorability = JSON.parse(JSON.stringify(vnState.favorability || {})); - saveData.vnState = JSON.parse(JSON.stringify(vnState)); - } else { - var node = simulatorData && simulatorData.nodes && simulatorData.nodes[simState.currentNode]; - saveData.chapter = (node ? (node.stage || "大" + node.year) : ""); - saveData.nodeId = simState.currentNode; - saveData.stats = JSON.parse(JSON.stringify(simState.stats)); - saveData.simState = JSON.parse(JSON.stringify(simState)); - } - - var saves = loadAllSaves(scriptId); - saves[slotIndex] = saveData; - saveAllSaves(scriptId, saves); - renderSaveSlots(scriptId); - showToast("存档成功 ✓"); -} - -function overwriteSlot(scriptId, slotIndex) { - saveToSlot(scriptId, slotIndex); -} - -function loadFromSlot(scriptId, slotIndex) { - var saves = loadAllSaves(scriptId); - var saveData = saves[slotIndex]; - if (!saveData) return; - - if (scriptId === "huluxiaxing") { - if (saveData.vnState) { - vnState = JSON.parse(JSON.stringify(saveData.vnState)); - localStorage.setItem("mathAppVNSave", JSON.stringify(vnState)); - } - hide("scriptSquare"); hide("simulatorContainer"); - show("lifeSubNav"); show("storyContainer"); - document.querySelectorAll(".life-sub-btn").forEach(function (b) { b.classList.toggle("active", b.dataset.life === "story"); }); - currentLifeTab = "story"; - startStoryEngine(); - } else { - if (saveData.simState) { - simState = JSON.parse(JSON.stringify(saveData.simState)); - saveSimState(); - } - hide("scriptSquare"); hide("storyContainer"); - show("lifeSubNav"); show("simulatorContainer"); - document.querySelectorAll(".life-sub-btn").forEach(function (b) { b.classList.toggle("active", b.dataset.life === "simulator"); }); - currentLifeTab = "simulator"; - startSimulator(); - } - - closeSavePanel(); - showToast("读档成功 ✓"); -} - -function deleteSlot(scriptId, slotIndex) { - var saves = loadAllSaves(scriptId); - saves[slotIndex] = null; // 保留空位 - saveAllSaves(scriptId, saves); - renderSaveSlots(scriptId); - showToast("已删除存档"); -} - -// 兼容旧版存档按钮 -function saveVNState() { - var scriptId = "huluxiaxing"; - // 自动保存到槽位0 - saveToSlot(scriptId, 0); -} - -// ============================================================ -// 结局画廊 (Ending Gallery — v8.3) -// ============================================================ -const ALL_ENDINGS = [ - { id: "truth", name: "真相守望", icon: "🔮", desc: "成为葫芦数学世界首位居民与共创者" }, - { id: "master", name: "数理宗师", icon: "📚", desc: "葫芦侠成为一代宗师,数堂发扬光大" }, - { id: "wander", name: "逍遥江湖", icon: "🍃", desc: "远离纷争,在江湖中自由游历" }, - { id: "sail", name: "出海远航", icon: "⛵", desc: "随欧阳侠远赴新大陆,开创东西方交流" }, - { id: "hero", name: "悲情英雄", icon: "⚔️", desc: "坚守信念,孤身面对审查与命运" }, - { id: "fall", name: "堕落之路", icon: "🌑", desc: "在权力与诱惑中迷失自我" }, - { id: "jail", name: "冤狱", icon: "⛓", desc: "被冤入狱,含恨而终" }, - { id: "void", name: "虚无", icon: "🌫", desc: "发现真相后精神崩溃,消失于江湖" }, - { id: "math_phd", name: "数学博士", icon: "🔬", desc: "坚持学术之路,走向研究巅峰" }, - { id: "quant", name: "量化交易员", icon: "💰", desc: "用数学模型在金融市场叱咤风云" }, - { id: "teacher", name: "高中教师", icon: "📝", desc: "回到家乡,培养下一代数学人才" }, - { id: "data_sci", name: "数据科学家", icon: "📊", desc: "在互联网公司用数据改变世界" } -]; - -function getUnlockedEndings() { - try { - var raw = localStorage.getItem("mathAppUnlockedEndings"); - return raw ? JSON.parse(raw) : []; - } catch (e) { return []; } -} - -function unlockEnding(endingId) { - var endings = getUnlockedEndings(); - if (endings.indexOf(endingId) < 0) { - endings.push(endingId); - localStorage.setItem("mathAppUnlockedEndings", JSON.stringify(endings)); - } -} - -function openGallery() { - document.getElementById("galleryOverlay").classList.add("show"); - renderGallery(); -} - -function closeGallery() { - document.getElementById("galleryOverlay").classList.remove("show"); -} - -function renderGallery() { - var unlocked = getUnlockedEndings(); - var progressEl = document.getElementById("galleryProgress"); - progressEl.textContent = unlocked.length + "/" + ALL_ENDINGS.length; - - var grid = document.getElementById("galleryGrid"); - grid.innerHTML = ALL_ENDINGS.map(function (e) { - var isUnlocked = unlocked.indexOf(e.id) >= 0; - if (isUnlocked) { - return ''; - } else { - return ''; - } - }).join(""); -} diff --git a/static/js/app.min.js b/static/js/app.min.js deleted file mode 100644 index 672d294..0000000 --- a/static/js/app.min.js +++ /dev/null @@ -1,1263 +0,0 @@ -let currentModule = "发现"; -let currentVideoUrl = ""; -let currentVideoTitle = ""; -let currentDiscipline = "all"; -let currentSubTab = "recommend"; -let currentLifeTab = "story"; -let allVideos = []; -let allKnowledgeCards = []; -let storyData = null; -let simulatorData = null; -let currentContext = { type: "", title: "" }; -let userId = localStorage.getItem("mathAppUserId") || generateUserId(); -let userFavorites = { video: {}, knowledge: {} }; -let userAvatar = localStorage.getItem("mathAppAvatar") || "葫"; -let userAvatarColor = localStorage.getItem("mathAppAvatarColor") || "#A0B9D8"; -let vnState = { -currentNode: null, -stats: { shuli: 10, xiayi: 10, xinzhi: 10 }, -favorability: { "欧阳侠": 0, "Eddie侠": 0, "厚朴侠": 0, "三力思侠": 0, "溜溜侠": 0, "孔锝侠": 0, "星侠": 0, "甲亢侠": 0 } -}; -let simState = { -currentNode: null, -stats: { gpa: 10, interest: 10, skill: 10 }, -history: [] -}; -const SIM_STAT_LABELS = { gpa: "绩点", interest: "兴趣", skill: "技能" }; -const SIM_STAT_COLORS = { gpa: "#A0B9D8", interest: "#DAB8B0", skill: "#B8D0C9" }; -const SIM_STAT_EMOJIS = { gpa: "📊", interest: "❤️", skill: "🔧" }; -function generateUserId() { -var id = "user_" + Date.now().toString(36) + "_" + Math.random().toString(36).substr(2, 6); -localStorage.setItem("mathAppUserId", id); -return id; -} -const STAT_LABELS = { shuli: "数理根基", xiayi: "侠义声望", xinzhi: "心志韧性" }; -const STAT_COLORS = { shuli: "#A0B9D8", xiayi: "#DAB8B0", xinzhi: "#B8D0C9" }; -const STAT_EMOJIS = { shuli: "📐", xiayi: "⚔️", xinzhi: "🧘" }; -function detectChapter(nodeId) { -if (!nodeId) return 1; -var m = nodeId.match(/^ch(\d+)/); -return m ? parseInt(m[1]) : 1; -} -const MODULE_COLORS = { -"发现": { color: "#A0B9D8", gradient: "linear-gradient(135deg, #A0B9D8, #8AA3C2)", lightBg: "#DCE6F0", name: "发现 · 值得一看的数学内容" }, -"知识": { color: "#B8D0C9", gradient: "linear-gradient(135deg, #B8D0C9, #A0BBAF)", lightBg: "#E4EFEB", name: "知识 · 数学发现" }, -"数学人生": { color: "#DAB8B0", gradient: "linear-gradient(135deg, #DAB8B0, #C9A39A)", lightBg: "#F2E4DF", name: "数学人生 · 大学选择模拟" }, -"我的": { color: "#C5CAD0", gradient: "linear-gradient(135deg, #C5CAD0, #AEB3B9)", lightBg: "#EDEEF0", name: "我的 · 个人中心" } -}; -const MODULE_BANNERS = { -"发现": { icon: "◎", title: "发现值得一看的数学内容", sub: "从 12 个专业分区进入,直接查看该方向的视频内容" }, -"知识": { icon: "≡", title: "用数学视角理解世界", sub: "把复杂概念变成轻量、好懂、适合反复刷的知识卡片" }, -"数学人生": { icon: "◇", title: "葫芦侠行 · 互动剧情", sub: "八章完整剧情 | 四属性系统 | 多结局 | 北宋数学武侠世界" }, -"我的": { icon: "✦", title: "你的学习空间", sub: "查看资料、收藏、记录和剧情存档" } -}; -const MODULE_CSS_CLASS = { -"发现": "shuren", "知识": "shushuo", "数学人生": "shusi", "我的": "shuwu" -}; -const DISCIPLINE_ICONS = { -"计算机": { icon: "💻", symbol: "⌨", color: "#fff", bg: "#8BA4C0" }, -"机械工程": { icon: "⚙️", symbol: "⚙", color: "#fff", bg: "#B8A99A" }, -"电子信息": { icon: "📡", symbol: "⚡", color: "#fff", bg: "#9BB8AE" }, -"经济管理": { icon: "📊", symbol: "📈", color: "#fff", bg: "#C5A8A0" }, -"生物医学": { icon: "🧬", symbol: "❤", color: "#fff", bg: "#C4B0B8" }, -"化工材料": { icon: "🧪", symbol: "⚗", color: "#fff", bg: "#ACAEC5" }, -"人工智能": { icon: "🤖", symbol: "AI", color: "#fff", bg: "#95ABB8" }, -"微电子": { icon: "🔌", symbol: "μ", color: "#fff", bg: "#8BA4C0" }, -"自动化": { icon: "🦾", symbol: "⚙", color: "#fff", bg: "#BCA898" }, -"能源动力": { icon: "🔥", symbol: "⚡", color: "#fff", bg: "#A3B4A5" }, -"环境工程": { icon: "🌱", symbol: "✿", color: "#fff", bg: "#A8B8A8" }, -"数学建模": { icon: "📐", symbol: "Σ", color: "#fff", bg: "#9CAEC4" }, -"通用": { icon: "📚", symbol: "◎", color: "#fff", bg: "#ADB5BD" } -}; -const AVATAR_COLORS = ["#A0B9D8","#B8D0C9","#DAB8B0","#C5CAD0","#B8A99A","#9BB8AE","#C4B0B8","#ACAEC5"]; -const SCRIPTS = [ -{ -id: "huluxiaxing", -name: "葫芦侠行", -icon: "📖", -desc: "北宋年间,紫金山下秉公习武堂。你扮演葫芦侠,用数学武学闯荡江湖。", -meta: "93节点 · 8章 · 8+结局", -status: "ready", -cssClass: "ss-card-wuxia", -engine: "story" -}, -{ -id: "simulator", -name: "数学专业模拟器", -icon: "🎓", -desc: "跑完四年大学数学专业的全流程。选课、考试、分流、毕业——你的每个选择决定结局。", -meta: "8学期 · 大三专业分流 · 多结局", -status: "beta", -cssClass: "ss-card-simulator", -engine: "simulator" -}, -{ -id: "mathbti", -name: "数学人格测试", -icon: "🧮", -desc: "12道场景题,发现你的数学人格类型——你是「梦境探索者」还是「博弈游侠」?", -meta: "12题 · 4轴测评 · 12人格", -status: "ready", -cssClass: "ss-card-mathbti", -engine: "mathbti" -} -]; -const COMING_SOON = [ -{ name: "数学侦探录", icon: "🔍", desc: "民国上海,用数学破案", color: "#B8A99A" }, -{ name: "星际数探", icon: "🔭", desc: "22世纪太空舰队首席数理官", color: "#9BB8AE" }, -{ name: "算学千年", icon: "📜", desc: "穿越古代,亲历数学史", color: "#ACAEC5" }, -{ name: "数学创业记", icon: "🚀", desc: "硅谷数学PhD辍学创业", color: "#C4B0B8" } -]; -const SPRITE_MAP = { -"葫芦侠": "https://coresg-normal.trae.ai/api/ide/v1/text_to_image?prompt=young%20Chinese%20Song%20dynasty%20scholar%20with%20a%20gourd%20hanging%20at%20his%20waist%2C%20simple%20blue-gray%20robes%2C%20gentle%20expression%2C%20clean%20ink%20wash%20painting%20style%20on%20soft%20background%2C%20morandi%20muted%20colors&image_size=portrait_4_3", -"欧阳侠": "https://coresg-normal.trae.ai/api/ide/v1/text_to_image?prompt=diligent%20young%20Chinese%20scholar%20reading%20a%20book%20at%20dawn%2C%20warm%20candle%20light%2C%20ink%20wash%20style%2C%20wearing%20simple%20robes%2C%20soft%20morandi%20beige%20and%20blue%20tones&image_size=portrait_4_3", -"溜溜侠": "https://coresg-normal.trae.ai/api/ide/v1/text_to_image?prompt=mysterious%20young%20Chinese%20female%20warrior%20with%20a%20large%20black%20dog%2C%20veiled%20face%2C%20dark%20grey%20robes%2C%20ink%20wash%20painting%20style%2C%20morandi%20muted%20dark%20tones%2C%20cool%20atmosphere&image_size=portrait_4_3", -"孔锝侠": "https://coresg-normal.trae.ai/api/ide/v1/text_to_image?prompt=strict%20Chinese%20martial%20arts%20instructor%2C%20tall%20and%20upright%2C%20stern%20but%20kind%20eyes%2C%20ink%20wash%20style%2C%20wearing%20neat%20grey-blue%20robes%2C%20morandi%20muted%20tones&image_size=portrait_4_3", -"Eddie侠": "https://coresg-normal.trae.ai/api/ide/v1/text_to_image?prompt=young%20Chinese%20scholar%20with%20copper%20coin%20spinning%20on%20finger%2C%20playful%20expression%2C%20ink%20wash%20style%2C%20soft%20morandi%20tones&image_size=portrait_4_3", -"厚朴侠": "https://coresg-normal.trae.ai/api/ide/v1/text_to_image?prompt=elegant%20Chinese%20theater%20actor%2C%20dramatic%20pose%2C%20traditional%20costume%2C%20ink%20wash%20style%2C%20morandi%20muted%20warm%20tones&image_size=portrait_4_3", -"星侠": "https://coresg-normal.trae.ai/api/ide/v1/text_to_image?prompt=gentle%20Chinese%20female%20scholar%20with%20warm%20smile%2C%20ink%20wash%20style%2C%20flowing%20robes%2C%20morandi%20soft%20pink%20and%20grey%20tones&image_size=portrait_4_3", -"甲亢侠": "https://coresg-normal.trae.ai/api/ide/v1/text_to_image?prompt=passionate%20young%20Chinese%20debater%2C%20animated%20gestures%2C%20ink%20wash%20style%2C%20morandi%20muted%20tones%2C%20intense%20eyes&image_size=portrait_4_3" -}; -const BG_MAP = { -"紫金山": "https://coresg-normal.trae.ai/api/ide/v1/text_to_image?prompt=ancient%20Chinese%20academy%20on%20misty%20Purple%20Mountain%2C%20traditional%20architecture%20with%20courtyards%2C%20morning%20fog%2C%20ink%20wash%20painting%20style%2C%20morandi%20muted%20greens%20and%20greys&image_size=landscape_4_3", -"石室": "https://coresg-normal.trae.ai/api/ide/v1/text_to_image?prompt=underground%20stone%20chamber%20turned%20study%20room%2C%20candle%20lit%2C%20wooden%20shelves%20with%20scrolls%2C%20murals%20on%20rock%20walls%2C%20warm%20atmosphere%2C%20ink%20wash%20style%2C%20morandi%20warm%20greys&image_size=landscape_4_3", -"姑苏": "https://coresg-normal.trae.ai/api/ide/v1/text_to_image?prompt=Suzhou%20ancient%20canal%20town%20in%20spring%2C%20stone%20bridges%2C%20willow%20trees%2C%20traditional%20white%20wall%20buildings%2C%20gentle%20rain%2C%20ink%20wash%20painting%20style%2C%20morandi%20muted%20blue-grey%20tones&image_size=landscape_4_3", -"京城": "https://coresg-normal.trae.ai/api/ide/v1/text_to_image?prompt=ancient%20Chinese%20capital%20city%2C%20grand%20government%20hall%2C%20solemn%20atmosphere%2C%20ink%20wash%20style%2C%20morandi%20muted%20grey%20tones&image_size=landscape_4_3" -}; -const CHAPTER_BG = { -1: "紫金山", -2: "紫金山", -3: "紫金山", -4: "石室", -5: "紫金山", -6: "石室", -7: "石室", -8: "京城" -}; -document.addEventListener("DOMContentLoaded", async function () { -await loadStoryData(); -await loadUserFavorites(); -loadVNSave(); -switchModule("发现"); -}); -function switchModule(module) { -currentModule = module; -currentDiscipline = "all"; -currentSubTab = "recommend"; -var colors = MODULE_COLORS[module] || MODULE_COLORS["发现"]; -document.documentElement.style.setProperty("--accent", colors.color); -document.documentElement.style.setProperty("--accent-light", colors.lightBg); -document.documentElement.style.setProperty("--accent-gradient", colors.gradient); -var brandIcon = document.querySelector(".brand-icon-sm"); -if (brandIcon) { brandIcon.textContent = "葫"; brandIcon.style.background = userAvatarColor; } -document.querySelectorAll(".tab-btn").forEach(function (btn) { -btn.classList.toggle("active", btn.dataset.module === module); -}); -hideAllPanels(); -if (module === "发现") { -show("searchHeader"); -show("subNav"); -show("videoList"); -document.querySelectorAll(".sub-nav-btn").forEach(function (btn) { -btn.classList.toggle("active", btn.dataset.tab === "recommend"); -}); -currentSubTab = "recommend"; -loadVideos("数人"); -return; -} else if (module === "知识") { -show("sectionTitleBar"); -show("shushuoHero"); -show("shushuoTopics"); -show("shushuoFeed"); -document.getElementById("contentTitle").textContent = colors.name; -loadKnowledgeCards(); -return; -} else if (module === "数学人生") { -show("scriptSquare"); -renderScriptSquare(); -return; -} else if (module === "我的") { -show("sectionTitleBar"); -show("profilePage"); -document.getElementById("contentTitle").textContent = colors.name; -loadProfileData(); -return; -} -} -function hideAllPanels() { -hide("searchHeader"); hide("subNav"); hide("sectionTitleBar"); hide("lifeSubNav"); -hide("moduleBanner"); hide("shushuoHero"); hide("shushuoTopics"); -hide("shushuoFeed"); hide("storyContainer"); hide("simulatorContainer"); -hide("disciplineGridContainer"); hide("disciplineActiveHeader"); -hide("disciplineGrid"); hide("categoryFilter"); -hide("profilePage"); hide("scriptSquare"); -hide("loadingArea"); hide("videoList"); -} -function show(id) { var el = document.getElementById(id); if (el) el.style.display = (id === "videoList" ? "grid" : id === "subNav" ? "flex" : "block"); } -function hide(id) { var el = document.getElementById(id); if (el) el.style.display = "none"; } -function updateBanner(module) { -var info = MODULE_BANNERS[module] || MODULE_BANNERS["发现"]; -var el = document.getElementById("moduleBanner"); -if (!el) return; -el.style.display = "flex"; -el.className = "module-banner " + (MODULE_CSS_CLASS[module] || "shuren"); -el.innerHTML = ''; -} -function handleSearch() { -var q = document.getElementById("videoSearch").value.toLowerCase().trim(); -if (!q) { renderVideoList(allVideos); return; } -renderVideoList(allVideos.filter(function (v) { -return (v.title && v.title.toLowerCase().indexOf(q) >= 0) || -(v.description && v.description.toLowerCase().indexOf(q) >= 0) || -(v.discipline && v.discipline.toLowerCase().indexOf(q) >= 0) || -(v.author && v.author.toLowerCase().indexOf(q) >= 0); -})); -} -function switchSubTab(tab) { -currentSubTab = tab; -document.querySelectorAll(".sub-nav-btn").forEach(function (btn) { btn.classList.toggle("active", btn.dataset.tab === tab); }); -var gridCtn = document.getElementById("disciplineGridContainer"); -var videoList = document.getElementById("videoList"); -var activeHdr = document.getElementById("disciplineActiveHeader"); -var catFilter = document.getElementById("categoryFilter"); -if (gridCtn) gridCtn.style.display = "none"; -if (activeHdr) activeHdr.style.display = "none"; -if (catFilter) catFilter.style.display = "none"; -if (tab === "discipline") { -if (videoList) videoList.style.display = "none"; -renderDisciplineGrid(); -} else if (tab === "hot") { -if (videoList) videoList.style.display = "grid"; -var sorted = [].concat(allVideos).sort(function (a, b) { return (hashStr(b.title) % 1000) - (hashStr(a.title) % 1000); }); -renderVideoList(sorted); -} else { -if (videoList) videoList.style.display = "grid"; -renderVideoList(allVideos); -} -} -function renderDisciplineGrid() { -var gridCtn = document.getElementById("disciplineGridContainer"); -var grid = document.getElementById("disciplineGrid"); -var videoList = document.getElementById("videoList"); -if (videoList) videoList.style.display = "none"; -var keys = Object.keys(DISCIPLINE_ICONS).filter(function (d) { return d !== "通用"; }); -grid.innerHTML = keys.map(function (d) { -var info = DISCIPLINE_ICONS[d]; -return '
' -+ '
' + (info.symbol || info.icon) + '
' -+ '
' + esc(d) + '
'; -}).join(""); -gridCtn.style.display = "block"; -grid.style.display = "grid"; -} -function selectDiscipline(d) { -document.getElementById("disciplineGridContainer").style.display = "none"; -document.getElementById("categoryFilter").style.display = "none"; -document.getElementById("videoList").style.display = "grid"; -document.getElementById("disciplineActiveHeader").style.display = "flex"; -document.getElementById("activeDisciplineName").textContent = d; -renderVideoList(allVideos.filter(function (v) { return v.discipline === d; })); -} -function backToDisciplineGrid() { -document.getElementById("disciplineActiveHeader").style.display = "none"; -document.getElementById("categoryFilter").style.display = "none"; -renderDisciplineGrid(); -} -async function loadKnowledgeCards(topic) { -show("loadingArea"); -hide("shushuoFeed"); -var url = "/api/knowledge"; -if (topic) url += "?topic=" + encodeURIComponent(topic); -try { -var resp = await fetch(url); -var result = await resp.json(); -hide("loadingArea"); -if (result.code === 0) { -allKnowledgeCards = result.data || []; -var topics = result.topics || []; -renderShushuoTopics(topics); -renderShushuoFeed(allKnowledgeCards); -} -} catch (e) { -hide("loadingArea"); -} -} -function renderShushuoTopics(topics) { -var scrollEl = document.getElementById("topicsScroll"); -scrollEl.innerHTML = ('全部') + -topics.map(function (t) { -return '' + esc(t) + ''; -}).join(""); -} -function renderShushuoFeed(cards) { -show("shushuoFeed"); -updateShushuoHero(); -var feed = document.getElementById("shushuoFeed"); -feed.innerHTML = cards.map(function (c) { -var isFav = userFavorites.knowledge[c.id] || false; -return '
' -+ '
' -+ '
' + (c.icon || "📖") + '
' -+ '
' + esc(c.question || "") + '
' -+ '' -+ '
' -+ '
' + esc(c.title) + '
' -+ '
' + esc(c.description) + '
' -+ '
'; -}).join(""); -} -function filterShushuoByTopic(topic) { -document.querySelectorAll(".topic-pill").forEach(function (p) { -var is = p.textContent.trim() === (topic || "全部"); -p.classList.toggle("active", is); -}); -if (topic) { -loadKnowledgeCards(topic); -} else { -loadKnowledgeCards(); -} -document.querySelector(".content-area").scrollTo({ top: 0, behavior: "smooth" }); -} -function openKnowledgeDetail(cardId) { -var card = allKnowledgeCards.find(function (c) { return c.id === cardId; }); -if (!card) return; -setAIContext("knowledge", card.title); -recordHistory("knowledge", card.id, card.title, card.icon || "📖"); -openChat(); -quickAsk(card.title + "是什么?能给我详细讲讲吗?"); -} -async function toggleFavorite(type, targetId, title, cover) { -var isFav = (userFavorites[type] || {})[targetId]; -if (isFav) { -try { -await fetch("/api/favorites", { -method: "DELETE", -headers: { "Content-Type": "application/json" }, -body: JSON.stringify({ user_id: userId, target_type: type, target_id: targetId }) -}); -if (userFavorites[type]) delete userFavorites[type][targetId]; -showToast("已取消收藏"); -} catch (e) { /* ignore */ } -} else { -try { -await fetch("/api/favorites", { -method: "POST", -headers: { "Content-Type": "application/json" }, -body: JSON.stringify({ user_id: userId, target_type: type, target_id: targetId, title: title, cover: cover }) -}); -if (!userFavorites[type]) userFavorites[type] = {}; -userFavorites[type][targetId] = true; -showToast("已加入收藏"); -} catch (e) { /* ignore */ } -} -if (currentModule === "知识" && type === "knowledge") { -renderShushuoFeed(allKnowledgeCards); -} else if (currentModule === "发现" && type === "video") { -renderVideoList(allVideos); -} -} -async function loadUserFavorites() { -try { -var resp = await fetch("/api/favorites?user_id=" + userId); -var result = await resp.json(); -if (result.code === 0) { -userFavorites = { video: {}, knowledge: {} }; -(result.data || []).forEach(function (f) { -if (!userFavorites[f.target_type]) userFavorites[f.target_type] = {}; -userFavorites[f.target_type][f.target_id] = true; -}); -} -} catch (e) { userFavorites = { video: {}, knowledge: {} }; } -} -async function recordHistory(type, targetId, title, cover) { -try { -await fetch("/api/history", { -method: "POST", -headers: { "Content-Type": "application/json" }, -body: JSON.stringify({ user_id: userId, target_type: type, target_id: targetId, title: title, cover: cover }) -}); -} catch (e) { /* ignore */ } -} -function showToast(message) { -var toast = document.getElementById("globalToast"); -if (!toast) { -toast = document.createElement("div"); -toast.id = "globalToast"; -toast.style.cssText = "position:fixed;bottom:100px;left:50%;transform:translateX(-50%);background:rgba(0,0,0,0.78);color:#fff;padding:8px 20px;border-radius:20px;font-size:13px;z-index:9999;pointer-events:none;transition:opacity 0.3s;opacity:0;"; -document.body.appendChild(toast); -} -toast.textContent = message; -toast.style.opacity = "1"; -clearTimeout(toast._timer); -toast._timer = setTimeout(function () { toast.style.opacity = "0"; }, 1500); -} -async function loadProfileData() { -try { -var [statsResp, profileResp] = await Promise.all([ -fetch("/api/user/stats?user_id=" + userId), -fetch("/api/user/profile?user_id=" + userId) -]); -var stats = (await statsResp.json()).data || { watch_count: 0, favorite_count: 0 }; -var profile = (await profileResp.json()).data || {}; -var statNums = document.querySelectorAll(".profile-stat-num"); -if (statNums.length >= 3) { -statNums[0].textContent = stats.watch_count || 0; -statNums[1].textContent = stats.favorite_count || 0; -statNums[2].textContent = "2"; // 剧情存档数保持固定 -} -var avatar = document.getElementById("profileAvatar"); -if (avatar) { -avatar.textContent = (profile.avatar_emoji || userAvatar); -avatar.style.background = (profile.avatar_color || userAvatarColor); -userAvatar = profile.avatar_emoji || userAvatar; -userAvatarColor = profile.avatar_color || userAvatarColor; -} -var nameEl = document.getElementById("profileName"); -if (nameEl) nameEl.textContent = (profile.nickname || "游客用户"); -var idEl = document.getElementById("profileId"); -if (idEl) idEl.textContent = "ID: " + userId.substring(0, 14); -} catch (e) { /* ignore */ } -} -function showFavorites() { -loadFavoritesList(); -} -async function loadFavoritesList() { -showProfileListPanel("我的收藏"); -var panel = document.getElementById("profileListContent"); -panel.innerHTML = '
'; -try { -var resp = await fetch("/api/favorites?user_id=" + userId); -var result = await resp.json(); -var items = result.data || []; -if (items.length === 0) { -panel.innerHTML = '

还没有收藏

去发现有趣的内容吧

'; -} else { -panel.innerHTML = items.map(function (f) { -return '
' -+ '
' + (f.cover || "◎") + '
' -+ '
' + esc(f.title) + '
' + f.target_type + ' · ' + (f.created_at || "").substring(0, 10) + '
' -+ '' -+ '
'; -}).join(""); -} -} catch (e) { -panel.innerHTML = '
⚠️

加载失败

'; -} -} -function showHistory() { -loadHistoryList(); -} -async function loadHistoryList() { -showProfileListPanel("观看记录"); -var panel = document.getElementById("profileListContent"); -panel.innerHTML = '
'; -try { -var resp = await fetch("/api/history?user_id=" + userId + "&limit=30"); -var result = await resp.json(); -var items = result.data || []; -if (items.length === 0) { -panel.innerHTML = '
👀

还没有观看记录

去看看视频和知识卡片吧

'; -} else { -panel.innerHTML = items.map(function (h) { -return '
' -+ '
' + (h.cover || "◎") + '
' -+ '
' + esc(h.title) + '
' + h.target_type + ' · ' + (h.watched_at || "").substring(0, 16) + '
' -+ '
'; -}).join(""); -} -} catch (e) { -panel.innerHTML = '
⚠️

加载失败

'; -} -} -function showProfileListPanel(title) { -var panel = document.getElementById("profileListPanel"); -var titleEl = document.getElementById("profileListTitle"); -if (titleEl) titleEl.textContent = title; -if (panel) panel.style.display = "block"; -var profilePage = document.getElementById("profilePage"); -if (profilePage) profilePage.style.display = "none"; -} -function hideProfileListPanel() { -var panel = document.getElementById("profileListPanel"); -if (panel) panel.innerHTML = ""; -if (panel) panel.style.display = "none"; -var profilePage = document.getElementById("profilePage"); -if (profilePage) profilePage.style.display = "block"; -loadProfileData(); -} -async function removeFavorite(type, targetId) { -await toggleFavorite(type, targetId, "", ""); -loadFavoritesList(); -loadProfileData(); -} -function editNickname() { -var name = prompt("输入新昵称:", document.querySelector(".profile-name").textContent); -if (name && name.trim()) { -var trimmed = name.trim().substring(0, 12); -fetch("/api/user/profile", { -method: "POST", -headers: { "Content-Type": "application/json" }, -body: JSON.stringify({ user_id: userId, nickname: trimmed }) -}).then(function () { -var el = document.querySelector(".profile-name"); -if (el) el.textContent = trimmed; -showToast("昵称已更新"); -}); -} -} -function updateShushuoHero() { -document.getElementById("heroTitle").textContent = "数学不是公式,是你理解世界的方式"; -document.getElementById("heroDesc").textContent = "每天一个数学概念,让思维变得更清晰"; -var el = document.querySelector(".hero-stats"); -if (el) el.innerHTML = "📖 " + allKnowledgeCards.length + "个概念⏱ 阅读约" + (allKnowledgeCards.length * 5) + "分钟"; -} -function adjustColor(hex, amount) { -var num = parseInt(hex.replace("#",""), 16); -var r = Math.min(255, (num >> 16) + amount); -var g = Math.min(255, ((num >> 8) & 0x00FF) + amount); -var b = Math.min(255, (num & 0x0000FF) + amount); -return "#" + ((r << 16) | (g << 8) | b).toString(16).padStart(6, "0"); -} -async function loadVideos(module, discipline) { -show("loadingArea"); -document.getElementById("videoList").innerHTML = ""; -var url = "/api/videos?module=" + encodeURIComponent(module); -if (discipline && discipline !== "all") url += "&discipline=" + encodeURIComponent(discipline); -try { -var resp = await fetch(url); -var result = await resp.json(); -hide("loadingArea"); -allVideos = result.data || []; -if (allVideos.length > 0) { -renderVideoList(allVideos); -} else { -document.getElementById("videoList").innerHTML = '
📭

暂无相关视频

后续将由各学院志愿者上传

'; -} -} catch (e) { -hide("loadingArea"); -document.getElementById("videoList").innerHTML = '
⚠️

加载失败

请稍后重试

'; -} -} -function renderVideoList(videos) { -var list = document.getElementById("videoList"); -if (!videos || videos.length === 0) { list.innerHTML = '
📭

暂无相关视频

'; return; } -list.style.display = "grid"; -list.innerHTML = videos.map(function (v) { -var avatarColor = AVATAR_COLORS[hashStr(v.author || "") % AVATAR_COLORS.length]; -var initial = (v.author || "?").replace(/学院|·|同学|系/g, "").slice(0, 1); -var dur = v.duration_min || 5; -var icon = v.discipline_icon && v.discipline_icon !== "·" ? v.discipline_icon : "◎"; -var coverBg = "hsl(" + (hashStr(v.discipline || "") % 360) + ", 40%, 90%)"; -var isFav = userFavorites.video[v.id] || false; -return '
' -+ '
' + icon + '
' + dur + ':00
' -+ '' -+ '
' -+ '
' + esc(v.title) + '
' -+ '
' + initial + '' -+ '' + esc(v.author) + '' -+ '
👁️' + ((hashStr(v.title) % 1000) + 100) + '
'; -}).join(""); -} -function openVideo(id, title, desc, url, author, cat) { -currentVideoUrl = url; -currentVideoTitle = title; -document.getElementById("videoModalTitle").textContent = title; -document.getElementById("videoModalDesc").textContent = desc; -document.getElementById("videoModalAuthor").innerHTML = author; -document.getElementById("videoModalCat").innerHTML = cat || "通用"; -document.getElementById("videoLinkBtn").style.display = url ? "block" : "none"; -document.getElementById("videoPlaceholderText").textContent = url ? "点击下方按钮查看完整课程视频" : "视频链接待补充"; -var favBtn = document.getElementById("videoModalFav"); -var vid = parseInt(id); -favBtn.textContent = (userFavorites.video[vid]) ? "★ 已收藏" : "☆ 收藏"; -favBtn.dataset.vid = vid; -document.getElementById("videoModal").classList.add("show"); -setAIContext("video", title); -recordHistory("video", vid, title, cat || "◎"); -} -function closeVideo() { document.getElementById("videoModal").classList.remove("show"); } -function openVideoLink() { if (currentVideoUrl) window.open(currentVideoUrl, "_blank"); } -function toggleVideoFav() { -var btn = document.getElementById("videoModalFav"); -var vid = parseInt(btn.dataset.vid); -var title = currentVideoTitle; -toggleFavorite("video", vid, title, "◎"); -btn.textContent = (userFavorites.video[vid]) ? "★ 已收藏" : "☆ 收藏"; -} -async function loadStoryData() { -try { -var resp = await fetch("/api/story"); -var result = await resp.json(); -if (result.code === 0) storyData = result.data; -} catch (e) { console.error("故事数据加载失败:", e); } -} -function loadVNSave() { -try { var s = localStorage.getItem("mathAppVNSave"); if (s) vnState = JSON.parse(s); } catch (e) {} -} -function saveVNState() { -localStorage.setItem("mathAppVNSave", JSON.stringify(vnState)); -alert("进度已存档!"); -} -function resetVNSave() { -var startNode = (storyData && storyData.start_node) || "ch1_start"; -vnState = { currentNode: startNode, stats: { shuli: 10, xiayi: 10, xinzhi: 10 }, favorability: { "欧阳侠": 0, "Eddie侠": 0, "厚朴侠": 0, "三力思侠": 0, "溜溜侠": 0, "孔锝侠": 0, "星侠": 0, "甲亢侠": 0 } }; -localStorage.removeItem("mathAppVNSave"); -navigateVN(startNode); -} -function applyVNStats(effects) { -if (!effects) return; -if (effects.shuli) vnState.stats.shuli = (vnState.stats.shuli || 10) + effects.shuli; -if (effects.xiayi) vnState.stats.xiayi = (vnState.stats.xiayi || 10) + effects.xiayi; -if (effects.xinzhi) vnState.stats.xinzhi = (vnState.stats.xinzhi || 10) + effects.xinzhi; -if (effects.intelligence !== undefined) vnState.stats.shuli = (vnState.stats.shuli || 10) + effects.intelligence; -if (effects.charm !== undefined) vnState.stats.xiayi = (vnState.stats.xiayi || 10) + effects.charm; -if (effects.stress !== undefined) vnState.stats.xinzhi = (vnState.stats.xinzhi || 10) - effects.stress; -if (effects.favorability) { -if (!vnState.favorability) vnState.favorability = {}; -for (var char in effects.favorability) { -vnState.favorability[char] = (vnState.favorability[char] || 0) + effects.favorability[char]; -} -} -} -function startStoryEngine() { -setAIContext("story", "葫芦侠行"); -var startNode = (storyData && storyData.start_node) || "ch1_start"; -var savedNode = vnState.currentNode; -if (savedNode && storyData && storyData.nodes && storyData.nodes[savedNode]) { -navigateVN(savedNode); -} else { -vnState.currentNode = startNode; -navigateVN(startNode); -} -} -function navigateVN(nodeId) { -if (!storyData || !storyData.nodes || !storyData.nodes[nodeId]) { -document.getElementById("storyContainer").innerHTML = '

剧情加载失败

节点未找到

'; -return; -} -var node = storyData.nodes[nodeId]; -vnState.currentNode = nodeId; -var container = document.getElementById("storyContainer"); -if (!container) return; -container.style.display = "block"; -container.scrollIntoView({ behavior: "smooth" }); -var ch = detectChapter(nodeId); -var chNames = ["","初入江湖","西部教习","数理论道","脑学初创","报国之路","暗流涌动","陋室修炼","真相之门"]; -var chName = chNames[ch] || ""; -var html = ''; -html += '
'; -html += '
第' + ch + '章 · ' + chName + '
'; -html += '
'; -html += '📐 数理根基: ' + (vnState.stats.shuli || 10) + ''; -html += '⚔️ 侠义声望: ' + (vnState.stats.xiayi || 10) + ''; -html += '🧘 心志韧性: ' + (vnState.stats.xinzhi || 10) + ''; -html += '
'; -html += '
'; -html += ''; -html += ''; -html += ''; -html += '
'; -var bgKey = CHAPTER_BG[ch] || "紫金山"; -var bgUrl = BG_MAP[bgKey] || ""; -if (node.background) bgUrl = BG_MAP[node.background] || node.background || bgUrl; -var spriteUrl = ""; -var characterName = node.character || ""; -if (node.sprite) { -spriteUrl = node.sprite; -} else if (characterName) { -var names = characterName.split("&"); -for (var ni = 0; ni < names.length; ni++) { -var n = names[ni].trim(); -if (SPRITE_MAP[n]) { spriteUrl = SPRITE_MAP[n]; break; } -} -} -html += '
'; -if (bgUrl) html += ''; -if (spriteUrl) html += '' + esc(characterName) + ''; -html += '
'; -if (characterName) html += '
' + esc(characterName) + '
'; -html += '
'; -html += '
'; -html += '
'; -container.innerHTML = html; -var sceneText = document.getElementById("sceneText"); -var text = node.scene || ""; -var i = 0; -function typeNext() { -if (i < text.length) { -sceneText.textContent += text.charAt(i); -i++; -setTimeout(typeNext, 25); -} else { -buildChoiceButtons(node.choices || []); -} -} -sceneText.textContent = ""; -typeNext(); -} -function buildChoiceButtons(choices) { -var area = document.getElementById("choicesArea"); -if (!area) return; -area.innerHTML = ""; -choices.forEach(function (c, idx) { -var btn = document.createElement("button"); -btn.className = "choice-btn"; -btn.style.animationDelay = (idx * 0.1) + "s"; -var effectsHTML = ""; -if (c.effects) { -var parts = []; -if (c.effects.shuli) parts.push("数理根基" + (c.effects.shuli > 0 ? "+" : "") + c.effects.shuli); -if (c.effects.xiayi) parts.push("侠义声望" + (c.effects.xiayi > 0 ? "+" : "") + c.effects.xiayi); -if (c.effects.xinzhi) parts.push("心志韧性" + (c.effects.xinzhi > 0 ? "+" : "") + c.effects.xinzhi); -if (c.effects.intelligence) parts.push("数理根基" + (c.effects.intelligence > 0 ? "+" : "") + c.effects.intelligence); -if (c.effects.charm) parts.push("侠义声望" + (c.effects.charm > 0 ? "+" : "") + c.effects.charm); -if (c.effects.stress) parts.push("心志韧性" + (c.effects.stress > 0 ? "+" : "") + c.effects.stress); -if (c.effects.favorability) { -for (var char in c.effects.favorability) { -var val = c.effects.favorability[char]; -parts.push(char + "好感" + (val > 0 ? "+" : "") + val); -} -} -if (parts.length > 0) effectsHTML = '[' + parts.join(" | ") + ']'; -} -btn.innerHTML = '' + esc(c.text) + '' + effectsHTML; -btn.onclick = (function (nextNode, effects) { -return function () { -applyVNStats(effects); -navigateVN(nextNode); -}; -})(c.next, c.effects); -area.appendChild(btn); -}); -} -function openChat() { document.getElementById("chatOverlay").classList.add("show"); document.getElementById("chatMessages").scrollTop = document.getElementById("chatMessages").scrollHeight; } -function closeChat() { document.getElementById("chatOverlay").classList.remove("show"); } -function quickAsk(q) { document.getElementById("chatInput").value = q; sendMessage(); } -async function sendMessage() { -var input = document.getElementById("chatInput"); -var message = input.value.trim(); -if (!message) return; -appendChat("user", message); -input.value = ""; -var typingId = appendTyping(); -try { -var resp = await fetch("/api/chat", { -method: "POST", headers: { "Content-Type": "application/json" }, -body: JSON.stringify({ message: message, context: currentContext }) -}); -var result = await resp.json(); -removeTyping(typingId); -appendChat("assistant", result.code === 0 ? result.reply : "抱歉,AI 助教暂时无法回复。"); -} catch (e) { -removeTyping(typingId); -appendChat("assistant", "网络异常,请稍后重试。"); -} -} -function setAIContext(type, title) { -currentContext = { type: type, title: title || "" }; -var hint = document.getElementById("chatContextHint"); -if (!hint) { -hint = document.createElement("div"); -hint.id = "chatContextHint"; -hint.style.cssText = "padding:6px 12px;font-size:11px;color:var(--accent);background:var(--accent-light);text-align:center;"; -var chatPanel = document.querySelector(".chat-panel"); -var header = document.querySelector(".chat-header"); -if (header && chatPanel) chatPanel.insertBefore(hint, header.nextSibling); -} -hint.textContent = currentContext.title ? "📍 当前:" + currentContext.title : ""; -hint.style.display = currentContext.title ? "block" : "none"; -} -function appendChat(role, text) { -var container = document.getElementById("chatMessages"); -var div = document.createElement("div"); -div.className = "chat-msg " + role; -div.innerHTML = '
' + esc(text) + "
"; -container.appendChild(div); -container.scrollTop = container.scrollHeight; -} -function appendTyping() { -var container = document.getElementById("chatMessages"); -var div = document.createElement("div"); -div.className = "chat-msg assistant typing-msg"; -div.innerHTML = '
'; -container.appendChild(div); -container.scrollTop = container.scrollHeight; -return div; -} -function removeTyping(el) { if (el && el.parentNode) el.remove(); } -function switchLifeTab(tab) { -currentLifeTab = tab; -document.querySelectorAll(".life-sub-btn").forEach(function (b) { b.classList.toggle("active", b.dataset.life === tab); }); -if (tab === "simulator") { -hide("storyContainer"); -show("simulatorContainer"); -setAIContext("simulator", "数学专业模拟器"); -startSimulator(); -} else { -hide("simulatorContainer"); -show("storyContainer"); -setAIContext("story", "葫芦侠行"); -startStoryEngine(); -} -} -async function loadSimulatorData() { -try { -var resp = await fetch("/api/simulator"); -var result = await resp.json(); -if (result.code === 0) simulatorData = result.data; -} catch (e) { console.error("模拟器数据加载失败:", e); } -} -async function loadSimSave() { -try { -var resp = await fetch("/api/simulator/save?user_id=" + userId); -var result = await resp.json(); -if (result.code === 0 && result.data) { -simState = result.data; -} -} catch (e) {} -} -function saveSimState() { -simState.history.push(simState.currentNode); -fetch("/api/simulator/save", { -method: "POST", -headers: { "Content-Type": "application/json" }, -body: JSON.stringify({ user_id: userId, state: simState }) -}).catch(function () {}); -} -function resetSimulator() { -simState = { currentNode: (simulatorData && simulatorData.start_node) || "y1_welcome", stats: { gpa: 10, interest: 10, skill: 10 }, history: [] }; -saveSimState(); -navigateSimulator(simState.currentNode); -} -async function startSimulator() { -if (!simulatorData) await loadSimulatorData(); -await loadSimSave(); -if (!simState.currentNode || !simulatorData || !simulatorData.nodes || !simulatorData.nodes[simState.currentNode]) { -resetSimulator(); -} else { -navigateSimulator(simState.currentNode); -} -} -function navigateSimulator(nodeId) { -if (!simulatorData || !simulatorData.nodes || !simulatorData.nodes[nodeId]) { -document.getElementById("simulatorContainer").innerHTML = '

剧情加载失败

'; -return; -} -var node = simulatorData.nodes[nodeId]; -simState.currentNode = nodeId; -saveSimState(); -var container = document.getElementById("simulatorContainer"); -if (!container) return; -container.style.display = "block"; -if (node.end) { -showEnding(node); -return; -} -container.scrollIntoView({ behavior: "smooth" }); -var ch = (node.year || 1); -var stage = node.stage || ""; -var html = ''; -html += '
'; -html += '
🎓 大' + ["", "一", "二", "三", "四"][ch] + ' · ' + stage + '
'; -html += '
'; -Object.keys(simState.stats).forEach(function (k) { -html += '' + SIM_STAT_EMOJIS[k] + ' ' + SIM_STAT_LABELS[k] + ': ' + simState.stats[k] + ''; -}); -html += '
'; -html += '
'; -html += ''; -html += ''; -html += ''; -html += ''; -html += '
'; -html += '
'; -if (node.character) html += '
' + esc(node.character) + '
'; -html += '
'; -html += '
'; -html += '
'; -container.innerHTML = html; -var textEl = document.getElementById("simText"); -var text = node.scene || ""; -var i = 0; -function typeNext() { -if (i < text.length) { -textEl.textContent += text.charAt(i); -i++; -setTimeout(typeNext, 20); -} else { -buildSimChoices(node.choices || []); -} -} -textEl.textContent = ""; -typeNext(); -} -function buildSimChoices(choices) { -var area = document.getElementById("simChoicesArea"); -if (!area) return; -area.innerHTML = ""; -choices.forEach(function (c, idx) { -var btn = document.createElement("button"); -btn.className = "sim-choice-btn"; -btn.style.animationDelay = (idx * 0.1) + "s"; -var effectsHTML = ""; -if (c.effects) { -var parts = []; -if (c.effects.gpa) parts.push((c.effects.gpa > 0 ? "+" : "") + c.effects.gpa + " 绩点"); -if (c.effects.interest) parts.push((c.effects.interest > 0 ? "+" : "") + c.effects.interest + " 兴趣"); -if (c.effects.skill) parts.push((c.effects.skill > 0 ? "+" : "") + c.effects.skill + " 技能"); -if (parts.length > 0) effectsHTML = '[' + parts.join(" | ") + ']'; -} -if (c.hint) effectsHTML += '💡 ' + esc(c.hint) + ''; -btn.innerHTML = '' + esc(c.text) + '' + effectsHTML; -btn.onclick = (function (nextNode, effects) { -return function () { -if (effects) applySimStats(effects); -navigateSimulator(nextNode); -}; -})(c.next, c.effects); -area.appendChild(btn); -}); -} -function applySimStats(effects) { -if (!effects) return; -if (effects.gpa) simState.stats.gpa = Math.max(0, Math.min(30, (simState.stats.gpa || 10) + effects.gpa)); -if (effects.interest) simState.stats.interest = Math.max(0, Math.min(30, (simState.stats.interest || 10) + effects.interest)); -if (effects.skill) simState.stats.skill = Math.max(0, Math.min(30, (simState.stats.skill || 10) + effects.skill)); -} -function showEnding(node) { -document.getElementById("endingOverlay").classList.add("show"); -var badges = { pure_math: "🔮", ai: "🤖", finance: "💰", research: "🔬", startup: "🚀", teacher: "📚", engineer: "⚙️", security: "🔒" }; -document.getElementById("endingBadge").textContent = badges[node.ending_type] || "🎓"; -document.getElementById("endingType").textContent = node.character || "结局"; -document.getElementById("endingScene").textContent = node.scene || ""; -document.getElementById("endingTitle").textContent = node.stage || ""; -if (node.ending_type) unlockEnding(node.ending_type); -var statsHtml = '
'; -Object.keys(simState.stats).forEach(function (k) { -statsHtml += '
' + simState.stats[k] + '
' + SIM_STAT_LABELS[k] + '
'; -}); -statsHtml += '
'; -document.getElementById("endingStats").innerHTML = statsHtml; -} -function closeEnding() { document.getElementById("endingOverlay").classList.remove("show"); } -var PICKER_EMOJIS = ["π","∑","∫","∞","Δ","∂","α","β","γ","λ","μ","σ","Ω","∇","⊥","⊕","⊗","≈","±","÷","√","∏","∃","∀","∈","⊂","∪","∩","ℵ","∮"]; -function openAvatarPicker() { -document.getElementById("avatarPickerOverlay").classList.add("show"); -var grid = document.getElementById("pickerEmojiGrid"); -grid.innerHTML = PICKER_EMOJIS.map(function (e) { -var selected = e === userAvatar ? ' style="border-color:var(--accent);background:var(--accent-light)"' : ""; -return ''; -}).join(""); -var colors = ["#A0B9D8","#B8D0C9","#DAB8B0","#C5CAD0","#B8A99A","#9BB8AE","#ACAEC5","#C4B0B8"]; -var colorRow = document.getElementById("pickerColorRow"); -colorRow.innerHTML = colors.map(function (c) { -var selected = c === userAvatarColor ? ' style="box-shadow:0 0 0 4px ' + c + '44"' : ""; -return ''; -}).join(""); -} -function selectAvatar(emoji) { -userAvatar = emoji; -localStorage.setItem("mathAppAvatar", emoji); -openAvatarPicker(); // refresh grid -updateAvatarUI(); -fetch("/api/user/profile", { -method: "POST", -headers: { "Content-Type": "application/json" }, -body: JSON.stringify({ user_id: userId, avatar_emoji: emoji }) -}); -} -function selectAvatarColor(color) { -userAvatarColor = color; -localStorage.setItem("mathAppAvatarColor", color); -openAvatarPicker(); -updateAvatarUI(); -fetch("/api/user/profile", { -method: "POST", -headers: { "Content-Type": "application/json" }, -body: JSON.stringify({ user_id: userId, avatar_color: color }) -}); -} -function updateAvatarUI() { -var av = document.getElementById("profileAvatar"); -if (av) { av.textContent = userAvatar; av.style.background = userAvatarColor; } -var brandIcon = document.querySelector(".brand-icon-sm"); -if (brandIcon) { brandIcon.textContent = "葫"; brandIcon.style.background = userAvatarColor; } -} -function closeAvatarPicker(e) { -if (e && e.target !== document.getElementById("avatarPickerOverlay")) return; -document.getElementById("avatarPickerOverlay").classList.remove("show"); -} -function esc(text) { -if (!text) return ""; -var div = document.createElement("div"); -div.textContent = text; -return div.innerHTML; -} -function hashStr(str) { -var hash = 0; -for (var i = 0; i < str.length; i++) { hash = ((hash << 5) - hash) + str.charCodeAt(i); hash |= 0; } -return Math.abs(hash); -} -function renderScriptSquare() { -try { -var heroCards = document.getElementById("ssHeroCards"); -if (!heroCards) return; -heroCards.innerHTML = SCRIPTS.map(function (s) { -var statusLabel = s.status === "beta" ? "开发中 · 抢先体验" : "已上线"; -return '
' -+ '
' -+ '
' -+ '
' + s.icon + '
' -+ '
' + esc(s.name) + '
' -+ '
' + esc(s.desc) + '
' -+ '
' + s.meta + '' + statusLabel + '
' -+ '
'; -}).join(""); -var comingCards = document.getElementById("ssComingCards"); -if (comingCards) { -comingCards.innerHTML = COMING_SOON.map(function (c) { -return '
' -+ '
' + c.icon + '
' -+ '
' -+ '
' + esc(c.name) + '
' -+ '
' + esc(c.desc) + '
' -+ '敬请期待' -+ '
'; -}).join(""); -} -updateContinueCard(); -} catch (e) { -console.error("剧本广场渲染失败:", e); -} -} -function updateContinueCard() { -try { -var continueEl = document.getElementById("ssContinue"); -if (!continueEl) return; -var vnSavedNode = vnState.currentNode; -var simSavedNode = simState.currentNode; -if (vnSavedNode && storyData && storyData.nodes && storyData.nodes[vnSavedNode]) { -var ch = detectChapter(vnSavedNode); -var chNames = ["","初入江湖","西部教习","数理论道","脑学初创","报国之路","暗流涌动","陋室修炼","真相之门"]; -document.getElementById("ssContinueTitle").textContent = "葫芦侠行 · 第" + ch + "章"; -document.getElementById("ssContinueSub").textContent = chNames[ch] || ""; -document.getElementById("ssContinueCard").onclick = function () { enterScript("huluxiaxing"); }; -continueEl.style.display = "block"; -} else if (simSavedNode && simulatorData && simulatorData.nodes && simulatorData.nodes[simSavedNode]) { -var node = simulatorData.nodes[simSavedNode]; -document.getElementById("ssContinueTitle").textContent = "数学专业模拟器"; -document.getElementById("ssContinueSub").textContent = (node.stage || "") + (node.year ? " · 大" + ["","一","二","三","四"][node.year] : ""); -document.getElementById("ssContinueCard").onclick = function () { enterScript("simulator"); }; -continueEl.style.display = "block"; -} else { -continueEl.style.display = "none"; -} -} catch (e) { -console.error("继续卡片更新失败:", e); -} -} -function enterScript(scriptId) { -hide("scriptSquare"); -if (scriptId === "huluxiaxing") { -setAIContext("story", "葫芦侠行"); -hide("simulatorContainer"); -show("lifeSubNav"); -show("storyContainer"); -var btns = document.querySelectorAll(".life-sub-btn"); -btns.forEach(function (b) { -b.classList.toggle("active", b.dataset.life === "story"); -}); -startStoryEngine(); -} else if (scriptId === "simulator") { -setAIContext("simulator", "数学专业模拟器"); -hide("storyContainer"); -show("lifeSubNav"); -show("simulatorContainer"); -var btns = document.querySelectorAll(".life-sub-btn"); -btns.forEach(function (b) { -b.classList.toggle("active", b.dataset.life === "simulator"); -}); -startSimulator(); -} else if (scriptId === "mathbti") { -window.location.href = "/mathbti"; -} -} -function backToSquare() { -hide("lifeSubNav"); -hide("storyContainer"); -hide("simulatorContainer"); -show("scriptSquare"); -renderScriptSquare(); -document.querySelector(".content-area").scrollTo({ top: 0, behavior: "smooth" }); -} -function getSaveKey(scriptId) { return "mathAppSave_" + scriptId; } -function loadAllSaves(scriptId) { -try { -var raw = localStorage.getItem(getSaveKey(scriptId)); -return raw ? JSON.parse(raw) : []; -} catch (e) { return []; } -} -function saveAllSaves(scriptId, saves) { -localStorage.setItem(getSaveKey(scriptId), JSON.stringify(saves)); -} -function openSavePanel() { -var scriptId = (currentLifeTab === "simulator") ? "simulator" : "huluxiaxing"; -document.getElementById("savePanelOverlay").classList.add("show"); -renderSaveSlots(scriptId); -} -function closeSavePanel() { -document.getElementById("savePanelOverlay").classList.remove("show"); -} -function renderSaveSlots(scriptId) { -var slots = document.getElementById("saveSlots"); -var saves = loadAllSaves(scriptId); -var MAX_SLOTS = 5; -var html = ""; -for (var i = 0; i < MAX_SLOTS; i++) { -var s = saves[i]; -if (s) { -var icon = scriptId === "simulator" ? "🎓" : "📖"; -var name = (scriptId === "simulator") ? "数学专业模拟器" : "葫芦侠行"; -var meta = s.chapter || ""; -var date = s.savedAt ? s.savedAt.substring(0, 16).replace("T", " ") : ""; -html += '
'; -html += '
' + icon + '
'; -html += '
'; -html += '
' + esc(name) + '
'; -html += '
' + esc(meta) + '' + date + '
'; -html += '
'; -html += '
'; -html += ''; -html += ''; -html += '
'; -} else { -html += '
'; -html += '+ 空槽位 ' + (i + 1) + ' — 点击保存'; -html += '
'; -} -} -slots.innerHTML = html; -} -function saveToSlot(scriptId, slotIndex) { -var saveData = { -savedAt: new Date().toISOString(), -scriptId: scriptId -}; -if (scriptId === "huluxiaxing") { -saveData.chapter = detectChapter(vnState.currentNode) ? "第" + detectChapter(vnState.currentNode) + "章" : ""; -saveData.nodeId = vnState.currentNode; -saveData.stats = JSON.parse(JSON.stringify(vnState.stats)); -saveData.favorability = JSON.parse(JSON.stringify(vnState.favorability || {})); -saveData.vnState = JSON.parse(JSON.stringify(vnState)); -} else { -var node = simulatorData && simulatorData.nodes && simulatorData.nodes[simState.currentNode]; -saveData.chapter = (node ? (node.stage || "大" + node.year) : ""); -saveData.nodeId = simState.currentNode; -saveData.stats = JSON.parse(JSON.stringify(simState.stats)); -saveData.simState = JSON.parse(JSON.stringify(simState)); -} -var saves = loadAllSaves(scriptId); -saves[slotIndex] = saveData; -saveAllSaves(scriptId, saves); -renderSaveSlots(scriptId); -showToast("存档成功 ✓"); -} -function overwriteSlot(scriptId, slotIndex) { -saveToSlot(scriptId, slotIndex); -} -function loadFromSlot(scriptId, slotIndex) { -var saves = loadAllSaves(scriptId); -var saveData = saves[slotIndex]; -if (!saveData) return; -if (scriptId === "huluxiaxing") { -if (saveData.vnState) { -vnState = JSON.parse(JSON.stringify(saveData.vnState)); -localStorage.setItem("mathAppVNSave", JSON.stringify(vnState)); -} -hide("scriptSquare"); hide("simulatorContainer"); -show("lifeSubNav"); show("storyContainer"); -document.querySelectorAll(".life-sub-btn").forEach(function (b) { b.classList.toggle("active", b.dataset.life === "story"); }); -currentLifeTab = "story"; -startStoryEngine(); -} else { -if (saveData.simState) { -simState = JSON.parse(JSON.stringify(saveData.simState)); -saveSimState(); -} -hide("scriptSquare"); hide("storyContainer"); -show("lifeSubNav"); show("simulatorContainer"); -document.querySelectorAll(".life-sub-btn").forEach(function (b) { b.classList.toggle("active", b.dataset.life === "simulator"); }); -currentLifeTab = "simulator"; -startSimulator(); -} -closeSavePanel(); -showToast("读档成功 ✓"); -} -function deleteSlot(scriptId, slotIndex) { -var saves = loadAllSaves(scriptId); -saves[slotIndex] = null; // 保留空位 -saveAllSaves(scriptId, saves); -renderSaveSlots(scriptId); -showToast("已删除存档"); -} -function saveVNState() { -var scriptId = "huluxiaxing"; -saveToSlot(scriptId, 0); -} -const ALL_ENDINGS = [ -{ id: "truth", name: "真相守望", icon: "🔮", desc: "成为葫芦数学世界首位居民与共创者" }, -{ id: "master", name: "数理宗师", icon: "📚", desc: "葫芦侠成为一代宗师,数堂发扬光大" }, -{ id: "wander", name: "逍遥江湖", icon: "🍃", desc: "远离纷争,在江湖中自由游历" }, -{ id: "sail", name: "出海远航", icon: "⛵", desc: "随欧阳侠远赴新大陆,开创东西方交流" }, -{ id: "hero", name: "悲情英雄", icon: "⚔️", desc: "坚守信念,孤身面对审查与命运" }, -{ id: "fall", name: "堕落之路", icon: "🌑", desc: "在权力与诱惑中迷失自我" }, -{ id: "jail", name: "冤狱", icon: "⛓", desc: "被冤入狱,含恨而终" }, -{ id: "void", name: "虚无", icon: "🌫", desc: "发现真相后精神崩溃,消失于江湖" }, -{ id: "math_phd", name: "数学博士", icon: "🔬", desc: "坚持学术之路,走向研究巅峰" }, -{ id: "quant", name: "量化交易员", icon: "💰", desc: "用数学模型在金融市场叱咤风云" }, -{ id: "teacher", name: "高中教师", icon: "📝", desc: "回到家乡,培养下一代数学人才" }, -{ id: "data_sci", name: "数据科学家", icon: "📊", desc: "在互联网公司用数据改变世界" } -]; -function getUnlockedEndings() { -try { -var raw = localStorage.getItem("mathAppUnlockedEndings"); -return raw ? JSON.parse(raw) : []; -} catch (e) { return []; } -} -function unlockEnding(endingId) { -var endings = getUnlockedEndings(); -if (endings.indexOf(endingId) < 0) { -endings.push(endingId); -localStorage.setItem("mathAppUnlockedEndings", JSON.stringify(endings)); -} -} -function openGallery() { -document.getElementById("galleryOverlay").classList.add("show"); -renderGallery(); -} -function closeGallery() { -document.getElementById("galleryOverlay").classList.remove("show"); -} -function renderGallery() { -var unlocked = getUnlockedEndings(); -var progressEl = document.getElementById("galleryProgress"); -progressEl.textContent = unlocked.length + "/" + ALL_ENDINGS.length; -var grid = document.getElementById("galleryGrid"); -grid.innerHTML = ALL_ENDINGS.map(function (e) { -var isUnlocked = unlocked.indexOf(e.id) >= 0; -if (isUnlocked) { -return ''; -} else { -return ''; -} -}).join(""); -} diff --git a/templates/index.html b/templates/index.html deleted file mode 100644 index fbfe896..0000000 --- a/templates/index.html +++ /dev/null @@ -1,386 +0,0 @@ - - - - - -葫芦数学 · Hulu Math - - - - - - -
- - -
-
- - 葫芦数学 -
-
- -
-
- - -
- - -
-
- 🔍 - -
-
- - - - - - - - - - - - - - -
- - -
- - - - - - - - - - - -
-
-

加载中...

-
- - -
- - - - - - - - - - - - - - - - - - -
- - - -
- - -
-
-
-
-
?
- AI 数学助教 -
- -
-
-
-
- 你好!我是数学桥梁计划的 AI 助教

- 试试问我:
- · 学数学对学医有什么用?
- · 数学在建筑里怎么用?
- · 为什么学数学很重要? -
-
-
-
- - - - -
-
- - -
-
-
- - -
-
-
-

视频标题

- -
-
-
-
-

点击下方按钮在浏览器中查看完整课程

-
-
-
-

-
- -- - -- -
-
-
- - - -
-
-
- - - - - -
-
-
选择头像
-
-
-
- - -
-
-
- - -
-
-
🎓
-

结局

-

-
-

- - -
-
- - -
-
-
- - 存档管理 -
-
- -
-
-
- - - - - - - diff --git a/vercel.json b/vercel.json index 5f493e9..016c15e 100644 --- a/vercel.json +++ b/vercel.json @@ -1,7 +1,8 @@ { "rewrites": [ - { "source": "/", "destination": "/templates/index.html" }, + { "source": "/", "destination": "/templates/desktop.html" }, { "source": "/mathbti", "destination": "/templates/mathbti.html" }, + { "source": "/web", "destination": "/templates/desktop.html" }, { "source": "/desktop", "destination": "/templates/desktop.html" } ], "headers": [