// ===== State ===== const API_BASE='',USER_ID=localStorage.getItem('mb_user_id')||('mb_'+Math.random().toString(36).slice(2,10)); localStorage.setItem('mb_user_id',USER_ID); let CP='home',allV=[],allK=[],kT=[],vD=[],cD='',curDim='',cT='',sD=null,sVN={currentNode:null,stats:{shuli:10,xiayi:10,xinzhi:10},fav:{}},simD=null,simS={currentNode:null,stats:{gpa:3,interest:0,skill:0}},favC={},chatCtx={},tt=null; let cardData=null,cardCache=null,_gachaBusy=false; let cardState={owned:[],draws:0,lastDaily:'',knowledgeRead:0,drawHistory:[]}; let _lang=localStorage.getItem('mb_lang')||'zh'; // ===== i18n ===== const I18N={zh:{home:'首页',discover:'发现',toolbox:'工具箱',knowledge:'知识卡片',story:'葫芦侠行',simulator:'数学人生',favorites:'我的收藏',profile:'个人中心',mine:'我的',search:'搜索内容...',guest:'游客用户', heroTitle:'葫芦数学 · 用玩的方式打开数学',heroSub:'数学不是公式,是你理解世界的方式',heroCTA:'开始探索 →', exploreTitle:'探索数学的万千可能',tryTitle:'试试这些', mbtiName:'MathBTI 数学人格',mbtiDesc:'12题发现你的数学人格,领养专属数学小人 →', toolboxName:'数学工具箱',toolboxDesc:'口算竞赛 · 计算器 · 符号查询 · 函数绘图 →', storyName:'葫芦侠行',storyDesc:'北宋数学武侠 · 8章93节点 · 8种结局 →', simName:'数学人生',simDesc:'四大主题 · 更多数学人生敬请期待 →', videoName:'发现视频',videoDesc:'五维能力地图 · 看视频点亮成长',cardName:'知识卡片',cardDesc:'8个数学话题', aiName:'AI 助教',aiDesc:'随时解答数学疑问', petTip:'每天使用工具箱和模拟器获取经验升级', dailyFactPrefix:'💡 数学冷知识:', discoverGreet:'Hello there. 这里收集了来自各专业志愿者的数学科普视频。', mapTitle:'五维能力地图',mapSub:'观看视频,点亮数学小人的五种能力',mapHint:'点击地图节点,进入对应维度的视频流', dimAll:'全部视频',dimVision:'数学眼光',dimHumanity:'数学人文',dimDetective:'数学侦探',dimModeling:'数学建模',dimConnection:'数学联结', claimVideo:'看完打卡',claimDone:'已学习',claimNeedPet:'先做 MathBTI 领养数学小人,再看视频点亮能力', filterAll:'全部',noVideo:'暂无视频',noCard:'暂无卡片',noResult:'无结果', loadFailVideo:'视频加载失败',loadFailCard:'卡片加载失败',loadFailProfile:'加载失败', loadMore:'下拉加载更多…',loadEnd:'— 已经到底啦 · 共 ',loadEndSuf:' 个视频 —', close:'关闭',save:'保存',saveOK:'保存成功',saveFail:'保存失败', toolboxHeader:'工具箱',toolboxSub:'动手做数学——口算、计算、查询、绘图', toolMathrace:'口算竞赛',toolMathraceDesc:'限时60秒,挑战你的心算速度', toolCalc:'计算器',toolCalcDesc:'支持加减乘除、幂运算、三角函数', toolSym:'数学符号查询',toolSymDesc:'点击符号查看含义和LaTeX写法', toolGraph:'函数图形绘图',toolGraphDesc:'输入 f(x),实时绘制函数曲线', knowledgeHeader:'知识卡片',knowledgeSub:'轻量数学发现,每天一个新概念', storyHeader:'葫芦侠行',storySub:'北宋数学武侠互动文字冒险 · 8章93节点 · 8+种结局', storyLoadFail:'故事加载失败',storyNodeNotFound:'节点未找到', simHeader:'数学人生',simSub:'四大主题 · 更多数学人生敬请期待', simLoadFail:'模拟器加载失败',simReset:'重新开始', favHeader:'我的收藏',favSub:'已收藏的视频和知识卡片', favEmpty:'暂无收藏',favEmptyHint:'在发现页点击 🤍 即可收藏', profileHeader:'个人中心',profileEdit:'编辑资料', profileAvatar:'头像:',profileNick:'昵称:', profileWatchCount:'观看次数',profileFavCount:'收藏数量', aiHeader:'AI 数学助教',aiWelcome:'你好!我是葫芦数学的AI助教 👋

试试问我:学数学对学医有什么用?', aiPlaceholder:'输入问题...',aiOffline:'抱歉,AI暂时无法回复',aiNetworkErr:'网络异常,请稍后重试', calcErr:'表达式有误',calcPlaceholder:'输入表达式 如 2+3*4', graphPlaceholder:'如 Math.sin(x) 或 x*x',graphBtn:'绘制', mrTitle:'准备好了吗?',mrPlaceholder:'输入答案',mrTimeout:'⏰ 时间到!得分:',mrCorrect:'正确',mrRight:'✅ 正确!+10分', symDetail:'👆 点击符号查看详情', mbtiFloat:'测测你的数学人格', aiAskBtn:'问问AI',aiAskSuffix:'是什么?', petLevelUp:'升级啦!Lv.{n}', staticHeroTitle:'葫芦数学 · 用玩的方式打开数学',staticHeroSub:'数学不是公式,是你理解世界的方式',staticHeroCTA:'开始探索 →', staticExploreTitle:'探索数学的万千可能',staticTryTitle:'试试这些', staticDiscoverGreet:'Hello there. 这里收集了来自各专业志愿者的数学科普视频。', staticToolboxTitle:'🧰 工具箱',staticToolboxSub:'动手做数学——口算、计算、查询、绘图', staticToolMathrace:'口算竞赛',staticToolMathraceDesc:'限时60秒,挑战你的心算速度', staticToolCalc:'计算器',staticToolCalcDesc:'支持加减乘除、幂运算、三角函数', staticToolSym:'数学符号查询',staticToolSymDesc:'点击符号查看含义和LaTeX写法', staticToolGraph:'函数图形绘图',staticToolGraphDesc:'输入 f(x),实时绘制函数曲线', staticKnowledgeTitle:'📚 知识卡片',staticKnowledgeSub:'轻量数学发现,每天一个新概念', staticStoryTitle:'⚔️ 葫芦侠行',staticStorySub:'北宋数学武侠互动文字冒险 · 8章93节点 · 8+种结局', staticSimTitle:'🧬 数学人生',staticSimSub:'四大主题 · 更多数学人生敬请期待', staticFavTitle:'❤️ 我的收藏',staticFavSub:'已收藏的视频和知识卡片', staticProfileTitle:'👤 个人中心', staticAIToggleTitle:'AI数学助教',staticAIHeader:'💬 AI 数学助教', staticAIWelcome:'你好!我是葫芦数学的AI助教 👋

试试问我:学数学对学医有什么用?', staticAIPlaceholder:'输入问题...',staticMbtiTooltip:'测测你的数学人格'}, en:{home:'Home',discover:'Discover',toolbox:'Toolbox',knowledge:'Cards',story:'HuLu Quest',simulator:'Math Life',favorites:'Favorites',profile:'Profile',mine:'Mine',search:'Search...',guest:'Guest', heroTitle:'Hulu Math · Play your way into math',heroSub:'Math isn\'t formulas. It\'s how you make sense of things.',heroCTA:'Start Exploring →', exploreTitle:'Explore Math',tryTitle:'Try These', mbtiName:'MathBTI Personality Test',mbtiDesc:'12 questions. One math soul. Meet your sprite. →', toolboxName:'Math Toolbox',toolboxDesc:'Mental Math · Calculator · Symbol Lookup · Graph →', storyName:'HuLu Quest',storyDesc:'A Song Dynasty math wuxia · 8 chapters · 8+ endings →', simName:'Math Life',simDesc:'Four themes. More math lives coming soon. →', videoName:'Discover Videos',videoDesc:'Five-dimension map · watch and grow',cardName:'Knowledge Cards',cardDesc:'8 math topics', aiName:'AI Tutor',aiDesc:'Ask anything. No judgment.', petTip:'Come back daily to earn EXP and level up your sprite.', dailyFactPrefix:'💡 Math Trivia: ', discoverGreet:'A hand-picked collection of math videos, curated by volunteers from across the sciences.', mapTitle:'Five-Dimension Ability Map',mapSub:'Watch videos and light up your math sprite\'s five abilities',mapHint:'Tap a map node to enter its video stream', dimAll:'All videos',dimVision:'Math Vision',dimHumanity:'Math Humanity',dimDetective:'Math Detective',dimModeling:'Math Modeling',dimConnection:'Math Connection', claimVideo:'Finish & earn',claimDone:'Learned',claimNeedPet:'Take MathBTI first to adopt a math sprite, then watch videos to grow it', filterAll:'All',noVideo:'No videos yet',noCard:'No cards yet',noResult:'No results', loadFailVideo:'Couldn\'t load videos',loadFailCard:'Couldn\'t load cards',loadFailProfile:'Couldn\'t load', loadMore:'Scroll for more…',loadEnd:'— That\'s all of them · ',loadEndSuf:' videos —', close:'Close',save:'Save',saveOK:'Saved',saveFail:'Couldn\'t save', toolboxHeader:'Toolbox',toolboxSub:'Get your hands dirty — race the clock, crunch numbers, explore symbols, draw curves', toolMathrace:'Mental Math',toolMathraceDesc:'60 seconds. How high can you score?', toolCalc:'Calculator',toolCalcDesc:'Quick arithmetic, powers, trig — whatever you need.', toolSym:'Symbol Lookup',toolSymDesc:'Tap a symbol to see what it means and how to write it in LaTeX.', toolGraph:'Function Grapher',toolGraphDesc:'Type any f(x) and watch its curve appear in real time.', knowledgeHeader:'Knowledge Cards',knowledgeSub:'One idea at a time. Bite-sized math, built for curiosity.', storyHeader:'HuLu Quest',storySub:'A math-fueled wuxia set in the Song Dynasty. 8 chapters. 8+ endings.', storyLoadFail:'Couldn\'t load the story',storyNodeNotFound:'Node not found', simHeader:'Math Life',simSub:'Four themes. More math lives coming soon.', simLoadFail:'Couldn\'t load the simulator',simReset:'Start fresh', favHeader:'My Favorites',favSub:'Your saved videos and cards', favEmpty:'Nothing saved yet',favEmptyHint:'Tap 🤍 on any video or card to save it here.', profileHeader:'Profile',profileEdit:'Edit Profile', profileAvatar:'Avatar:',profileNick:'Nickname:', profileWatchCount:'Videos watched',profileFavCount:'Favorites', aiHeader:'AI Math Tutor',aiWelcome:'Hey! I\'m your Hulu Math AI tutor 👋

Try asking: "Why do we even need calculus?"', aiPlaceholder:'Ask anything...',aiOffline:'The AI is taking a quick break. Try again in a moment.',aiNetworkErr:'Looks like you\'re offline. Check your connection and try again.', calcErr:'That doesn\'t look right',calcPlaceholder:'e.g. 2+3*4', graphPlaceholder:'e.g. Math.sin(x) or x*x',graphBtn:'Plot', mrTitle:'Ready?',mrPlaceholder:'Type your answer',mrTimeout:'⏰ Time\'s up! You scored ',mrCorrect:'right',mrRight:'✅ Correct! +10', symDetail:'👆 Tap any symbol to learn more', mbtiFloat:'Discover your Math Personality', aiAskBtn:'Ask AI',aiAskSuffix:'?', petLevelUp:'Level up! Now Lv.{n}', staticHeroTitle:'Hulu Math · Play your way into math',staticHeroSub:'Math isn\'t formulas. It\'s how you make sense of things.',staticHeroCTA:'Start Exploring →', staticExploreTitle:'Explore Math',staticTryTitle:'Try These', staticDiscoverGreet:'A hand-picked collection of math videos, curated by volunteers from across the sciences.', staticToolboxTitle:'🧰 Toolbox',staticToolboxSub:'Get your hands dirty — race the clock, crunch numbers, explore symbols, draw curves', staticToolMathrace:'Mental Math',staticToolMathraceDesc:'60 seconds. How high can you score?', staticToolCalc:'Calculator',staticToolCalcDesc:'Quick arithmetic, powers, trig — whatever you need.', staticToolSym:'Symbol Lookup',staticToolSymDesc:'Tap a symbol to see what it means and how to write it in LaTeX.', staticToolGraph:'Function Grapher',staticToolGraphDesc:'Type any f(x) and watch its curve appear in real time.', staticKnowledgeTitle:'📚 Knowledge Cards',staticKnowledgeSub:'One idea at a time. Bite-sized math, built for curiosity.', staticStoryTitle:'⚔️ HuLu Quest',staticStorySub:'A math-fueled wuxia set in the Song Dynasty. 8 chapters. 8+ endings.', staticSimTitle:'🧬 Math Life',staticSimSub:'Four themes. More math lives coming soon.', staticFavTitle:'❤️ My Favorites',staticFavSub:'Your saved videos and cards', staticProfileTitle:'👤 Profile', staticAIToggleTitle:'AI Math Tutor',staticAIHeader:'💬 AI Math Tutor', staticAIWelcome:'Hey! I\'m your Hulu Math AI tutor 👋

Try asking: "Why do we even need calculus?"', staticAIPlaceholder:'Ask anything...', staticMbtiTooltip:'Discover your Math Personality'}}; function t(k){const d=I18N[_lang];return (d&&d[k])||(I18N.zh[k]||k);} function rI18n(){ document.querySelectorAll('[data-i18n]').forEach(el=>{ const k=el.getAttribute('data-i18n'); const v=t(k); if(el.tagName==='INPUT'){el.placeholder=v} else if(el.hasAttribute('title')&&el.getAttribute('data-i18n-type')==='title'){el.title=v} else{el.innerHTML=v} }); } function switchLang(l){ _lang=l;localStorage.setItem('mb_lang',l); document.querySelectorAll('.lang-btn').forEach(b=>b.classList.toggle('active',b.dataset.lang===l)); document.getElementById('globalSearch').placeholder=t('search'); rI18n(); navigateTo(CP); api('/api/user/profile?user_id='+USER_ID).then(r=>{if(r.code===0&&r.data){document.getElementById('sidebarName').textContent=r.data.nickname||t('guest')}}).catch(()=>{}); } // ===== Sidebar ===== function toggleSidebar(){document.getElementById('sidebar').classList.toggle('open');document.getElementById('sidebarOverlay').classList.toggle('active')} function closeSidebar(){document.getElementById('sidebar').classList.remove('open');document.getElementById('sidebarOverlay').classList.remove('active')} // ===== Global Search ===== let searchTimeout; function handleGlobalSearch(){ clearTimeout(searchTimeout); searchTimeout=setTimeout(()=>{ const q=document.getElementById('globalSearch').value.trim().toLowerCase(); const el=document.getElementById('searchResults'); if(!q||q.length<1){el.classList.remove('show');return} const results=[]; allV.forEach(v=>{if((v.title||'').toLowerCase().includes(q)||(v.author||'').toLowerCase().includes(q)||(v.discipline||'').toLowerCase().includes(q))results.push({t:t('videoTitle')||'Video',title:v.title,id:v.id,type:'video'})}); allK.forEach(k=>{if((k.title||'').toLowerCase().includes(q)||(k.description||'').toLowerCase().includes(q))results.push({t:t('cardLabel')||'Card',title:k.title,id:k.id,type:'knowledge'})}); if(results.length===0){el.innerHTML='
'+t('noResult')+'
';el.classList.add('show');return} el.innerHTML=results.slice(0,8).map(r=>'
'+(r.t==='视频'?'Video':r.t==='知识'?'Card':r.t)+''+esc(r.title)+'
').join(''); el.classList.add('show'); },200); } function searchNavigate(type,id){ document.getElementById('globalSearch').value='';document.getElementById('searchResults').classList.remove('show'); if(type==='video'){navigateTo('discover');setTimeout(()=>openVideo(id,'','','','',''),500)} else if(type==='knowledge'){navigateTo('discover');setTimeout(()=>openKnowledgeDetail(id),500)} } document.addEventListener('click',e=>{if(!e.target.closest('.sidebar-search'))document.getElementById('searchResults').classList.remove('show')}); // ===== AI Panel ===== function toggleAIPanel(){ var panel=document.getElementById('aiPanel'); if(!panel)return; var modal=document.getElementById('toolModal'); if(modal)modal.classList.remove('show'); if(panel.classList.contains('open')){ panel.classList.remove('open'); }else{ panel.classList.add('open'); } } // ===== API ===== const _apiCache={},_CACHEABLE=['/api/videos','/api/knowledge','/api/story','/api/simulator','/api/math_personality']; async function api(p,o={}){const u=API_BASE+p,c={headers:{'Content-Type':'application/json'},...o};if(c.body&&typeof c.body==='object')c.body=JSON.stringify(c.body);const isGet=!c.method;if(isGet&&_CACHEABLE.some(x=>p.indexOf(x)===0)&&_apiCache[u])return _apiCache[u];const r=await fetch(u,c);const j=await r.json();if(isGet&&_CACHEABLE.some(x=>p.indexOf(x)===0))_apiCache[u]=j;return j} // ===== Route ===== function navigateTo(p){ CP=p;closeSidebar();closeToolModal();document.getElementById('searchResults').classList.remove('show'); // 离开首页时停止 Hero 动画循环,避免空转耗电 if(p!=='home'&&heroAnimId){cancelAnimationFrame(heroAnimId);heroAnimId=null} document.querySelectorAll('.nav-item').forEach(b=>b.classList.toggle('active',b.dataset.page===p)); document.querySelectorAll('.page').forEach(pg=>pg.classList.remove('active')); const t=document.getElementById('page-'+p);if(t)t.classList.add('active'); document.getElementById('mainContent').scrollTop=0; if(p==='home')loadHome();else if(p==='discover')loadDiscover();else if(p==='toolbox')loadToolbox(); else if(p==='knowledge')loadKnowledgePage();else if(p==='story')loadStory();else if(p==='simulator')loadSimulator();else if(p==='favorites')loadFavorites();else if(p==='profile')loadProfile();else if(p==='cards')loadCardsPage();else if(p==='mine')loadMine(); } // ===== Home (Desmos-style) ===== let heroAnimId=null; async function loadHome(){ chatCtx={}; // Hero canvas animation setTimeout(drawHeroCanvas,100); // Pet renderPetCard(); // Feature cards (2x2) document.getElementById('homeFeatureCards').innerHTML=[ {i:'🧬',n:t('mbtiName'),d:t('mbtiDesc'),click:"window.open('/mathbti','_blank')"}, {i:'🧰',n:t('toolboxName'),d:t('toolboxDesc'),click:"navigateTo('toolbox')"}, {i:'⚔️',n:t('storyName'),d:t('storyDesc'),click:"navigateTo('story')"}, {i:'🎮',n:t('simName'),d:t('simDesc'),click:"navigateTo('simulator')"} ].map(c=>'
'+c.i+'
'+c.n+'
'+c.d+'
').join(''); // Extra cards (1x3) document.getElementById('homeExtraCards').innerHTML=[ {i:'📺',n:t('videoName'),d:t('videoDesc'),click:"navigateTo('discover')"}, {i:'📚',n:t('cardName'),d:t('cardDesc'),click:"navigateTo('knowledge')"}, {i:'💬',n:t('aiName'),d:t('aiDesc'),click:"toggleAIPanel()"} ].map(c=>'
'+c.i+'
'+c.n+'
'+c.d+'
').join(''); // Daily fact const facts=['π的小数点后第762位开始连续出现六个9,被称为「费曼点」。','世界上最大的已知质数有超过4100万位数字。','哥德巴赫猜想:每个大于2的偶数都可以写成两个质数之和——至今未被证明。','四色定理:任何地图只需4种颜色就能让相邻区域颜色不同。1976年用计算机证明。','莫比乌斯带只有一面——拿一条纸带,扭转180°后粘起来试试。']; document.getElementById('homeDailyFact').innerHTML=t('dailyFactPrefix')+facts[Math.floor(Math.random()*facts.length)]; rCG(); } function drawHeroCanvas(){ const c=document.getElementById('heroCanvas');if(!c)return; // 防止重复进入首页叠加多个动画循环 if(heroAnimId){cancelAnimationFrame(heroAnimId);heroAnimId=null} const ctx=c.getContext('2d'),w=c.width,h=c.height; let t=0; function frame(){ ctx.clearRect(0,0,w,h); ctx.strokeStyle='#D6D1CA';ctx.lineWidth=1; for(let i=0;i0.7){ ctx.save();ctx.globalAlpha=Math.min(1,(Math.sin(t*0.3)-0.7)/0.3*0.5); ctx.strokeStyle='#A8907E';ctx.lineWidth=1; const gx=w-60,gy=h-30; ctx.beginPath(); // 上葫芦 (小) ctx.ellipse(gx,gy-28,12,9,0,0,Math.PI*2); // 下葫芦 (大) ctx.ellipse(gx,gy,16,13,0,0,Math.PI*2); ctx.stroke(); ctx.restore(); } t+=0.02;heroAnimId=requestAnimationFrame(frame); } frame(); } // ===== Discover: Five-Dimension Ability Map ===== const DIM5=[ {id:'vision',key:'眼光',label:'数学眼光',short:'眼光',icon:'👁',color:'#5B6AF0',x:50,y:12,desc:'洞察本质的直觉,一眼看穿背后的数学结构。',rep:'庞加莱'}, {id:'humanity',key:'人文',label:'数学人文',short:'人文',icon:'📜',color:'#B06A3B',x:17,y:36,desc:'思想溯源的厚度,理解数学背后的人与史。',rep:'希帕提娅'}, {id:'detective',key:'侦探',label:'数学侦探',short:'侦探',icon:'🔍',color:'#2E7D72',x:24,y:86,desc:'逻辑溯源的严谨,在迷雾中找到唯一真相。',rep:'图灵'}, {id:'modeling',key:'建模',label:'数学建模',short:'建模',icon:'🏗️',color:'#C47F3A',x:76,y:86,desc:'解决现实的力量,把公式变成改变世界的武器。',rep:'冯·诺依曼'}, {id:'connection',key:'联结',label:'数学联结',short:'联结',icon:'🔗',color:'#7B5EA7',x:83,y:36,desc:'跨界融合的通感,连接数学内外的一切。',rep:'牛顿'} ]; const DIM_EXTRA_VIDEOS=[ {id:9101,module:'数人',sub_category:'数学人文',discipline:'数学人文',discipline_icon:'📜',title:'赵爽弦图:《周髀算经》里的人类智慧',video_url:'https://www.bilibili.com/video/BV1example301',description:'从勾股定理的证明看数学如何成为文明的火种。赵爽的弦图,是数学人文的第一张草图。',author:'数学人文馆',duration_min:6}, {id:9102,module:'数人',sub_category:'数学人文',discipline:'数学人文',discipline_icon:'📜',title:'希帕提娅:为理性光芒守护到最后一刻',video_url:'https://www.bilibili.com/video/BV1example302',description:'一位女数学家在动荡年代坚持讲授数学,理解她,就理解了数学背后的信仰。',author:'数学人文馆',duration_min:7}, {id:9103,module:'数人',sub_category:'数学人文',discipline:'数学人文',discipline_icon:'📜',title:'图灵与恩尼格玛:密码背后的人与史',video_url:'https://www.bilibili.com/video/BV1example303',description:'从密码战争到计算机科学,图灵用逻辑改变了历史走向。',author:'数学人文馆',duration_min:8}, {id:9104,module:'数说',sub_category:'几何直觉',discipline:'数学眼光',discipline_icon:'👁',title:'向日葵、贝壳与斐波那契:用数学眼光看自然',video_url:'https://www.bilibili.com/video/BV1example304',description:'当你看一朵花不再只看到美,而是看到数列与结构,数学眼光就亮了。',author:'数学眼光馆',duration_min:5}, {id:9105,module:'数说',sub_category:'几何直觉',discipline:'数学眼光',discipline_icon:'👁',title:'城市建筑里的对称与黄金比例',video_url:'https://www.bilibili.com/video/BV1example305',description:'从古典建筑到现代地标,用数学眼光捕捉城市里隐藏的几何秩序。',author:'数学眼光馆',duration_min:5}, {id:9106,module:'数说',sub_category:'几何直觉',discipline:'数学眼光',discipline_icon:'👁',title:'庞加莱猜想:直觉如何带人类走进更高维度',video_url:'https://www.bilibili.com/video/BV1example306',description:'像庞加莱一样,在踏上公交车的一瞬间,看见隐藏在空间里的结构。',author:'数学眼光馆',duration_min:6} ]; function videoDimKey(v){ const t=(v.title||'')+(v.sub_category||'')+(v.discipline||''); if(/人文|赵爽|周髀|希帕提娅|数学史|恩尼格玛|战火|信仰|守护/.test(t))return'人文'; if(/地图|画不准|高斯绝妙|斐波那契|向日葵|黄金比例|对称|眼光|直觉|庞加莱/.test(t))return'眼光'; if(/强化学习|AlphaGo|人工智能|神经网络|深度学习|矩阵|GPU/.test(t))return'联结'; if(/密码|网络安全|统计|平均数|彩票|费米|图论|六度|二进制|卡尔曼|PLC|布尔|集成电路|雷达|多普勒|编译|形式语言|B\+树|数据库/.test(t))return'侦探'; if(/金融|风险|经济|市场|供应链|线性规划|物流|回归|建模|数学模型|反应动力学|传质|材料|气候变化|污水处理|质量平衡|种群|扩散|高斯模型|中子|AHP|Matlab|Python|数模|最优控制|机器人控制|自行车|齿轮|振动|有限元/.test(t))return'建模'; if(/傅里叶|麦克斯韦|采样|拉东|心电|DNA|信息论|水平集|渐开线|薛定谔|边界层|熵增|太阳能|空间群|光刻|半导体|计算机视觉|几何变换/.test(t))return'联结'; return'联结'; } function dimInfo(v){return DIM5.find(d=>d.key===videoDimKey(v))||DIM5[4]} function mergeAllV(list){ const arr=(list||[]).slice(); DIM_EXTRA_VIDEOS.forEach(e=>{if(!arr.some(v=>v.id===e.id))arr.push(e)}); const dm={}; arr.forEach(v=>{if(v.discipline&&!dm[v.discipline])dm[v.discipline]={discipline:v.discipline,discipline_icon:v.discipline_icon||'·'}}); vD=Object.values(dm); return arr; } async function loadDiscover(){ chatCtx={}; if(favP){await favP;favP=null} if(!allV.length){ renderSkeletons('discoverVideos',6); try{ const r=await api('/api/videos'); if(r.code===0)allV=mergeAllV(r.data||[]); }catch(e){ document.getElementById('discoverVideos').innerHTML='

'+t('loadFailVideo')+'

'; allV=mergeAllV([]); } }else{ allV=mergeAllV(allV); } rDF();rDimTabs();rDMap();rVCNP('discoverVideos',filterVideos()); } function dimVideoCount(k){return allV.filter(v=>dimInfo(v).key===k).length} function filterVideos(){return allV.filter(v=>(!curDim||dimInfo(v).key===curDim)&&(!cD||v.discipline===cD))} function rDMap(){ const el=document.getElementById('dimMap');if(!el)return; const pet=getPet(); const lines=DIM5.map((d,i)=>{ const n=DIM5[(i+1)%DIM5.length]; return ''; }).join(''); const spokes=DIM5.map(d=>'').join(''); el.innerHTML= '
'+t('mapTitle')+'
'+t('mapSub')+'
'+DIM5.map(d=>''+esc(d.label)+' · '+dimVideoCount(d.key)+'').join('')+'
'+ '
'+spokes+lines+''+ '
数学小人
'+ DIM5.map(d=>{const v=pet&&pet.dims?Math.min(100,Math.max(0,Number(pet.dims[d.key])||0)):null;return ''}).join('')+ '
'+ '
'+t('mapHint')+'
'; } function rDimTabs(){ const el=document.getElementById('dimTabs');if(!el)return; let h=''; DIM5.forEach(d=>{h+=''}); el.innerHTML=h; } function fDim(d){curDim=d;rDimTabs();rDMap();rVCNP('discoverVideos',filterVideos())} function scrollToVideos(){const el=document.getElementById('discoverVideos');if(el)el.scrollIntoView({behavior:'smooth',block:'start'})} function rDF(){let h='';vD.forEach(d=>{h+=''});document.getElementById('discoverFilters').innerHTML=h} function fV(d){cD=d;rDF();rVCNP('discoverVideos',filterVideos())} // ===== 视频卡片网格:无限滚动(每次12条) ===== const VC_PAGE=12; let vcList=[],vcCount=0,vcObserver=null; const UP_COLORS=['#5B6AF0','#818CF8','#F59E0B','#10B981','#EF4444','#8B5CF6','#EC4899','#14B8A6']; function hashStr(s){let h=0;for(let i=0;i=10000?(n/10000).toFixed(1)+'万':''+n} function fmtDur(m){m=parseInt(m)||0;return(m<10?'0'+m:''+m)+':00'} function rVCNP(cid,vids){ const c=document.getElementById(cid); vcList=vids||[];vcCount=0;c.innerHTML=''; if(!vcList.length){c.innerHTML='

'+t('noVideo')+'

';return} appendVC(cid); } function appendVC(cid){ const c=document.getElementById(cid); const batch=vcList.slice(vcCount,vcCount+VC_PAGE); if(!batch.length)return; vcCount+=batch.length; const old=document.getElementById('npLoadHint');if(old)old.remove(); c.insertAdjacentHTML('beforeend',batch.map(buildVC).join('')); if(vcCount'+t('loadMore')+''); if(!vcObserver){vcObserver=new IntersectionObserver(es=>{es.forEach(en=>{if(en.isIntersecting)appendVC('discoverVideos')})},{root:document.getElementById('mainContent'),rootMargin:'240px'})} vcObserver.observe(document.getElementById('npLoadHint')); }else if(vcList.length>VC_PAGE){ c.insertAdjacentHTML('beforeend','
'+t('loadEnd')+vcList.length+t('loadEndSuf')+'
'); } } function buildVC(v){ const dur=v.duration_min||5,disc=v.discipline||'',author=v.author||'',dim=dimInfo(v); const icon=v.discipline_icon&&v.discipline_icon!=='·'?v.discipline_icon:'📺'; const h=hashStr(disc||v.title); const coverBg='linear-gradient(135deg,hsl('+(h%360)+',45%,90%),hsl('+((h+30)%360)+',45%,84%))'; const views=hashStr(v.title)%50000+300,dms=hashStr(v.title+'#dm')%900; const upColor=UP_COLORS[hashStr(author)%UP_COLORS.length]; const initial=(author||'?').replace(/学院|·|同学|系/g,'').slice(0,1)||'?'; const faved=favC['video_'+v.id]; const click='onclick="openVideo('+v.id+',\''+esc(v.title)+'\',\''+esc(v.description||'')+'\',\''+esc(v.video_url||'')+'\',\''+esc(author)+'\',\''+esc(disc)+'\',\''+esc(dim.key)+'\')"'; const cover=v.cover_url ?'
' :'
'+icon+'
'; return '
'+ '
'+cover+ ''+fmtDur(dur)+''+ ''+ '
'+ '
'+dim.icon+' '+esc(dim.label)+'
'+esc(v.title)+'
'+ '
👁 '+fmtNum(views)+'💬 '+fmtNum(dms)+'
'+ '
'+esc(initial)+''+esc(author)+'
'+ '
'; } function openVideo(id,title,desc,url,author,disc,dimKey){ chatCtx={type:'video',title:title};rH('video',id,title); const found=allV.find(v=>v.id===id); if(!title&&found)title=found.title; if(!desc&&found)desc=found.description; const dim=dimKey?DIM5.find(d=>d.key===dimKey):(found?dimInfo(found):dimInfo({title:title,discipline:disc})); const pet=getPet(); const claims=getVideoClaims(); const claimed=!!claims['video_'+id]; const claimBox=pet ?'
'+(claimed ?'' :'')+'
' :'
'+t('claimNeedPet')+'
'; let h='
'+dim.icon+' '+esc(dim.label)+'

'+esc(title)+'

'; h+=url?'
':'
'; h+='

'+esc(desc||'No description')+'

'+esc(author)+''+esc(disc)+''+esc(dim.label)+'
'; h+=claimBox+'
'; document.getElementById('toolModalContent').innerHTML=h; document.getElementById('toolModal').classList.add('show'); } function getVideoClaims(){try{return JSON.parse(localStorage.getItem('mb_video_claims')||'{}')||{}}catch(e){return{}}} function claimVideo(id,dimKey,label){ if(!getPet())return; const claims=getVideoClaims(); if(claims['video_'+id])return; claims['video_'+id]=true; localStorage.setItem('mb_video_claims',JSON.stringify(claims)); addPetDim(dimKey,3); addPetExp(10); const btn=document.querySelector('.video-claim-btn'); if(btn){btn.disabled=true;btn.classList.add('claimed');btn.innerHTML='✅ '+t('claimDone')+' · '+esc(label)+' +3'} showToast(esc(label)+' +3 · 数学小人成长中'); rDMap(); } // ===== Knowledge Cards (standalone page) ===== async function loadKnowledgePage(){ chatCtx={type:'knowledge'}; if(allK.length){rKCFilters();rKCCards();rKP();return} try{const r=await api('/api/knowledge');if(r.code===0){allK=r.data||[];kT=r.topics||[];rKCFilters();rKCCards()}}catch(e){document.getElementById('knowledgeGrid').innerHTML='

卡片加载失败

'}} function rKCFilters(){let h='';kT.forEach(t=>{h+=''});document.getElementById('knowledgeFilters').innerHTML=h} function fKC(t){cT=t;rKCFilters();rKCCards()} function rKP(){ const bar=document.getElementById('knowledgeDrawProgress'); if(!bar)return; const readThisSession=(cardState.knowledgeRead||0)%3; const needMore=3-readThisSession; bar.style.display='flex'; if(needMore<=0){ document.getElementById('drawProgressText').textContent='🎉 可以抽卡了!点击去抽卡'; document.getElementById('drawProgressFill').style.width='100%'; bar.classList.add('draw-progress-ready'); }else{ document.getElementById('drawProgressText').textContent='再读 '+needMore+' 张知识卡片即可抽卡'; document.getElementById('drawProgressFill').style.width=(readThisSession/3*100)+'%'; bar.classList.remove('draw-progress-ready'); } } function rKCCards(){const cards=cT?allK.filter(c=>c.topic===cT):allK;const g=document.getElementById('knowledgeGrid');if(!cards||cards.length===0){g.innerHTML='

'+t('noCard')+'

';return}g.innerHTML=cards.map(c=>'
'+c.icon+'
'+esc(c.question)+'
'+esc(c.title)+'
'+esc(c.description)+'
'+esc(c.read_time)+'
').join('');setTimeout(rKP,100)} async function openKnowledgeDetail(id){chatCtx={type:'knowledge',title:''}; // 记录知识卡片阅读 cardState.knowledgeRead=(cardState.knowledgeRead||0)+1; if(cardState.knowledgeRead>=3&&cardState.knowledgeRead%3===0){ cardState.draws+=1; saveCardState(); }else{ saveCardState(); } rKP(); try{const r=await api('/api/knowledge/'+id);if(r.code===0){const c=r.data;chatCtx.title=c.title;document.getElementById('toolModalContent').innerHTML='
'+c.icon+'

'+esc(c.title)+'

'+esc(c.question)+'

'+esc(c.detail||c.description)+'

'+esc(c.read_time)+' · '+esc(c.topic||'')+'
'}}catch(e){}document.getElementById('toolModal').classList.add('show')} function closeToolModal(){var m=document.getElementById('toolModal');if(m)m.classList.remove('show')} // ===== Toolbox ===== function loadToolbox(){chatCtx={}} let toolState={}; function openTool(name){ let h=''; if(name==='mathrace'){toolState={score:0,total:0,time:60,timer:null,correct:0};h=rMR()} else if(name==='calculator')h=rCalc(); else if(name==='symbols')h=rSym(); else if(name==='grapher')h=rGraph(); document.getElementById('toolModalContent').innerHTML=h; document.getElementById('toolModal').classList.add('show'); if(name==='mathrace')startMRLoop(); if(name==='grapher')setTimeout(plotGraph,200); } function rMR(){return'
060s📝 0
准备好了吗?
'} function startMRLoop(){toolState={score:0,total:0,time:60,timer:null,correct:0};document.getElementById('mrScore').textContent='0';document.getElementById('mrTime').textContent='60s';document.getElementById('mrTotal').textContent='0';document.getElementById('mrResult').textContent='';document.getElementById('mrAnswer').value='';document.getElementById('mrAnswer').disabled=false;document.getElementById('mrAnswer').focus();genMR();toolState.timer=setInterval(()=>{toolState.time--;document.getElementById('mrTime').textContent=toolState.time+'s';document.getElementById('mrTimer').style.width=(toolState.time/60*100)+'%';if(toolState.time<=0){clearInterval(toolState.timer);document.getElementById('mrAnswer').disabled=true;document.getElementById('mrResult').innerHTML='⏰ 时间到!得分:'+toolState.score+' 正确:'+toolState.correct+'/'+toolState.total;addPetExp(toolState.score)}},1000);genMR()} function genMR(){const ops=['+','-','×'];const op=ops[Math.floor(Math.random()*3)];let a,b;if(op==='+'){a=Math.floor(Math.random()*90)+10;b=Math.floor(Math.random()*90)+10}else if(op==='-'){a=Math.floor(Math.random()*90)+10;b=Math.floor(Math.random()*a)}else{a=Math.floor(Math.random()*12)+2;b=Math.floor(Math.random()*12)+2}toolState.answer=op==='+'?a+b:op==='-'?a-b:a*b;toolState.total++;document.getElementById('mrTotal').textContent=toolState.total;document.getElementById('mrQuestion').textContent=a+' '+op+' '+b+' = ?';document.getElementById('mrAnswer').value='';document.getElementById('mrAnswer').focus()} function checkMR(){const ans=parseInt(document.getElementById('mrAnswer').value);if(isNaN(ans))return;if(ans===toolState.answer){toolState.score+=10;toolState.correct++;document.getElementById('mrResult').innerHTML='✅ '+t('mrRight')+''}else{document.getElementById('mrResult').innerHTML='❌ '+toolState.answer+''}document.getElementById('mrScore').textContent=toolState.score;if(toolState.time>0)setTimeout(genMR,600)} function rCalc(){return'
'+['(',')','C','÷','7','8','9','×','4','5','6','-','1','2','3','+','0','.','sin(','√('].map(k=>'').join('')+'
'} function calcKey(k){const i=document.getElementById('calcInput');if(k==='C')i.value='';else if(k==='÷')i.value+='/';else if(k==='×')i.value+='*';else if(k==='sin(')i.value+='Math.sin(';else if(k==='√(')i.value+='Math.sqrt(';else i.value+=k;i.focus()} function doCalc(){try{const v=eval(document.getElementById('calcInput').value);document.getElementById('calcResult').textContent=Number.isInteger(v)?v:v.toFixed(6)}catch(e){document.getElementById('calcResult').textContent='表达式有误'}} function rSym(){ const syms=[['π','Pi · 圆周率','3.14159... · \\pi'],['∞','无穷大','没有边界 · \\infty'],['Σ','求和符号','累加 · \\sum'],['∫','积分符号','面积/累积 · \\int'],['√','平方根','x²=a → x=√a · \\sqrt{}'],['Δ','变化量/判别式','差值△y/△x · \\Delta'],['∂','偏导数','多元函数求导 · \\partial'],['lim','极限','趋近于 · \\lim_{x \\to 0}'],['e','自然常数','≈2.71828 · e'],['i','虚数单位','i² = -1 · i'],['∏','连乘符号','累乘 · \\prod'],['∈','属于','元素∈集合 · \\in']]; return'
'+syms.map(s=>'').join('')+'
👆 点击符号查看详情
' } function sS(sym,name,latex){document.getElementById('symDetail').innerHTML=''+sym+'
'+name+'
LaTeX: '+latex+'
'} function rGraph(){return'
'} function plotGraph(){const c=document.getElementById('graphCanvas');if(!c)return;const ctx=c.getContext('2d');ctx.clearRect(0,0,c.width,c.height);ctx.strokeStyle='#D6D1CA';ctx.lineWidth=1;for(let i=0;i<=c.width;i+=50){ctx.beginPath();ctx.moveTo(i,0);ctx.lineTo(i,c.height);ctx.stroke()}for(let i=0;i<=c.height;i+=50){ctx.beginPath();ctx.moveTo(0,i);ctx.lineTo(c.width,i);ctx.stroke()}ctx.strokeStyle='#808C95';ctx.lineWidth=2;ctx.beginPath();const fn=new Function('x','return '+document.getElementById('graphFunc').value);const sx=c.width/2,sy=c.height/2,scale=30;for(let px=0;px-100&&py故事加载失败

'}} function rSN(ni){if(!sD||!sD.nodes||!sD.nodes[ni]){document.getElementById('storyScene').innerHTML='

节点未找到

';return}const n=sD.nodes[ni];sVN.currentNode=ni;localStorage.setItem('mb_story_save',JSON.stringify(sVN));const ch=dC(ni),cN=['','初入江湖','西部教习','数理论道','脑学初创','报国之路','暗流涌动','陋室修炼','真相之门'];document.getElementById('storyStats').innerHTML='📐 '+(sVN.stats.shuli||10)+'⚔️ '+(sVN.stats.xiayi||10)+'🧘 '+(sVN.stats.xinzhi||10)+''+ch+' · '+(cN[ch]||'')+'';document.getElementById('storyScene').innerHTML=(n.character?'
'+esc(n.character)+'
':'')+'
';document.getElementById('storyChoices').innerHTML='';if(tt)clearTimeout(tt);const te=document.getElementById('storyText'),tx=n.scene||'';let i=0;te.textContent='';function ty(){if(i{let h='';if(c.effects){const p=[];if(c.effects.shuli)p.push('数理根基'+(c.effects.shuli>0?'+':'')+c.effects.shuli);if(c.effects.xiayi)p.push('侠义声望'+(c.effects.xiayi>0?'+':'')+c.effects.xiayi);if(c.effects.xinzhi)p.push('心志韧性'+(c.effects.xinzhi>0?'+':'')+c.effects.xinzhi);if(p.length)h='['+p.join(' | ')+']'}return''}).join('')} function sC(next,ef){aSE(ef);rSN(next)} function aSE(ef){if(!ef)return;if(ef.shuli)sVN.stats.shuli=(sVN.stats.shuli||10)+ef.shuli;if(ef.xiayi)sVN.stats.xiayi=(sVN.stats.xiayi||10)+ef.xiayi;if(ef.xinzhi)sVN.stats.xinzhi=(sVN.stats.xinzhi||10)+ef.xinzhi;if(ef.favorability){if(!sVN.fav)sVN.fav={};for(const k in ef.favorability)sVN.fav[k]=(sVN.fav[k]||0)+ef.favorability[k]}} function resetStory(){if(tt)clearTimeout(tt);sVN={currentNode:null,stats:{shuli:10,xiayi:10,xinzhi:10},fav:{}};localStorage.removeItem('mb_story_save');loadStory()} function dC(ni){if(!ni)return 1;const m=ni.match(/^ch(\d+)/);return m?parseInt(m[1]):1} // ===== Simulator ===== async function loadSimulator(){loadMathLife()} async function loadLegacySimulator(){chatCtx={type:'simulator'};try{const r=await api('/api/simulator');if(r.code===0){simD=r.data;try{const sr=await api('/api/simulator/save?user_id='+USER_ID);if(sr.code===0&&sr.data)simS=sr.data}catch(e){}const sn=simD.start_node||'y1_welcome';if(!simS.currentNode||!simD.nodes||!simD.nodes[simS.currentNode])simS.currentNode=sn;rSiN(simS.currentNode)}}catch(e){document.getElementById('simScene').innerHTML='

模拟器加载失败

'}} function rSiN(ni){if(!simD||!simD.nodes||!simD.nodes[ni]){document.getElementById('simScene').innerHTML='

节点未找到

';return}const n=simD.nodes[ni];simS.currentNode=ni;api('/api/simulator/save',{method:'POST',body:{user_id:USER_ID,state:simS}}).catch(()=>{});document.getElementById('simStats').innerHTML='
'+(simS.stats.gpa||3).toFixed(1)+'
GPA
'+(simS.stats.interest||0)+'
'+t('interestLabel')+'
'+(simS.stats.skill||0)+'
'+t('skillLabel')+'
';document.getElementById('simScene').innerHTML=(n.stage?'
📅 '+esc(n.stage)+'
':'')+(n.character?'
'+esc(n.character)+'
':'')+'
'+esc(n.scene||'')+'
';document.getElementById('simChoices').innerHTML=(n.choices||[]).map(c=>'').join('')} function siC(next,ef){if(ef){if(ef.gpa)simS.stats.gpa=Math.max(0,Math.min(4,(simS.stats.gpa||3)+ef.gpa*.1));if(ef.interest)simS.stats.interest=(simS.stats.interest||0)+ef.interest;if(ef.skill)simS.stats.skill=(simS.stats.skill||0)+ef.skill}rSiN(next);addPetExp(5)} function resetSim(){simS={currentNode:null,stats:{gpa:3,interest:0,skill:0}};loadLegacySimulator()} // ===== Favorites ===== async function loadFavorites(){chatCtx={};try{await rFC()}catch(e){}try{const r=await api('/api/favorites?user_id='+USER_ID);const g=document.getElementById('favGrid');if(r.code===0){const f=r.data||[];if(f.length===0){g.innerHTML='

暂无收藏
在发现页点击 🤍 即可收藏

';return}g.innerHTML=f.map(fv=>'
'+(fv.target_type==='video'?'📺 视频':'📚 知识卡片')+'
'+esc(fv.title)+'
').join('')}}catch(e){document.getElementById('favGrid').innerHTML='

加载失败

'}} async function rFC(){try{const r=await api('/api/favorites?user_id='+USER_ID);if(r.code===0){favC={};(r.data||[]).forEach(f=>{favC[f.target_type+'_'+f.target_id]=true})}}catch(e){}} async function tF(type,id,title,cover){if(favC[type+'_'+id]){await api('/api/favorites',{method:'DELETE',body:{user_id:USER_ID,target_type:type,target_id:id}});favC[type+'_'+id]=false;showToast('🗑 '+t('close')+' ✔')}else{await api('/api/favorites',{method:'POST',body:{user_id:USER_ID,target_type:type,target_id:id,title:title,cover:cover}});favC[type+'_'+id]=true;showToast('❤ '+t('saveOK'))}if(CP==='discover')fV(cD);else if(CP==='favorites')loadFavorites()} async function rF(type,id){await api('/api/favorites',{method:'DELETE',body:{user_id:USER_ID,target_type:type,target_id:id}});loadFavorites()} // ===== History ===== async function rH(type,id,title){try{await api('/api/history',{method:'POST',body:{user_id:USER_ID,target_type:type,target_id:id,title:title}})}catch(e){}} // ===== Profile ===== async function loadProfile(){chatCtx={};try{const[pr,sr]=await Promise.all([api('/api/user/profile?user_id='+USER_ID),api('/api/user/stats?user_id='+USER_ID)]);const p=pr.code===0?pr.data:{nickname:'游客用户',avatar_emoji:'葫',avatar_color:'#5B6AF0'};const s=sr.code===0?sr.data:{watch_count:0,favorite_count:0};document.getElementById('profileContent').innerHTML='
'+esc(p.avatar_emoji||'葫')+'
'+esc(p.nickname||'游客用户')+'
ID: '+USER_ID+'
'+s.watch_count+'
观看次数
'+s.favorite_count+'
收藏数量
编辑资料
头像:
昵称:
'}catch(e){document.getElementById('profileContent').innerHTML='

加载失败

'}} async function sP(){const n=document.getElementById('editNickname').value.trim();const a=document.getElementById('editAvatar').value.trim();try{await api('/api/user/profile',{method:'POST',body:{user_id:USER_ID,nickname:n,avatar_emoji:a}});document.getElementById('sidebarName').textContent=n||'游客用户';document.getElementById('sidebarAvatar').textContent=a||'葫';alert('保存成功');if(CP==='mine')loadMine();else loadProfile()}catch(e){alert('保存失败')}} // ===== Card Collection Page ===== async function loadCardsPage(){ chatCtx={}; document.getElementById('cardsProgress').textContent=cardState.owned.length+'/16'; const drawBtn=document.getElementById('cardsDrawBtn'); const drawCount=document.getElementById('cardsDrawCount'); drawCount.textContent=cardState.draws; drawBtn.style.display=cardState.draws>0?'inline-flex':'none'; const grid=document.getElementById('cardsCollectionGrid'); await loadCardData(); const allCodes=getAllCardCodes(); if(!allCodes.length){grid.innerHTML='

卡片数据加载中…

';return} let h=''; allCodes.forEach(code=>{ const info=getCardInfo(code); const clan=info?getClanInfo(info.clan):null; const clanColor=clan?clan.color:'#A8907E'; const owned=cardState.owned.indexOf(code)!==-1; h+='
'+ (owned ?'
'+portraitHTML(info,24)+'
'+(info?info.name:'???')+'
'+code+'
'+(clan?clan.emoji+' '+clan.name:'')+'
'+stats5HTML(info) :'
'+portraitHTML(info,24)+'
???
未获得
'+(clan?clan.emoji+' '+clan.name:'未知')+'
')+ '
'; }); grid.innerHTML=h; } // ===== Mine Page (我的 — 个人中心 + 收藏 + 卡册入口) ===== async function loadMine(){ chatCtx={}; const c=document.getElementById('mineContent'); if(!c)return; let p={nickname:'游客用户',avatar_emoji:'葫',avatar_color:'#5B6AF0'}; let s={watch_count:0,favorite_count:0}; try{ const[pr,sr]=await Promise.all([api('/api/user/profile?user_id='+USER_ID),api('/api/user/stats?user_id='+USER_ID)]); if(pr.code===0&&pr.data)p=pr.data; if(sr.code===0&&sr.data)s=sr.data; }catch(e){} c.innerHTML= ''+ '
'+esc(p.avatar_emoji||'葫')+'
'+esc(p.nickname||'游客用户')+'
ID: '+USER_ID+'
'+ '
'+s.watch_count+'
观看次数
'+s.favorite_count+'
收藏数量
'+ '
编辑资料
头像:
昵称:
'+ '
'+ '
❤️
我的收藏
已收藏的视频和知识卡片
'+ '
🎴
我的卡册
收集全部16位数学人物 · '+cardState.owned.length+'/16
'+ '
'; } // ===== Pet Experience ===== function getPet(){ let pet=null; try{ const a=localStorage.getItem('mb_pet'); if(a)pet=JSON.parse(a); if(!pet||!pet.type){ const b=localStorage.getItem('mathbrain_pet'); if(b)pet=JSON.parse(b); } }catch(e){} if(!pet||!pet.type)return null; pet.name=pet.name||pet.type; pet.level=pet.level||1; pet.exp=pet.exp||0; pet.dims=pet.dims||{眼光:10,人文:10,侦探:10,建模:10,联结:10}; return pet; } function savePet(pet){if(!pet)return;localStorage.setItem('mb_pet',JSON.stringify(pet));localStorage.setItem('mathbrain_pet',JSON.stringify(pet))} function renderPetCard(){ const pet=getPet(),pa=document.getElementById('homePetArea'); if(!pa)return; if(!pet){pa.innerHTML='';return} pa.innerHTML='
'+pet.icon+'
'+esc(pet.name)+'
🎓 '+esc(pet.type)+' · Lv.'+pet.level+' · '+pet.exp+'/100 exp
'+DIM5.map(d=>{const v=Math.min(100,Math.max(0,Number((pet.dims||{})[d.key])||0));return '
'+d.icon+''+esc(d.short)+'
'+v+'
'}).join('')+'
'+t('petTip')+'
'; } function addPetDim(dimKey,amt){ const pet=getPet();if(!pet)return false; pet.dims=pet.dims||{眼光:10,人文:10,侦探:10,建模:10,联结:10}; pet.dims[dimKey]=Math.min(100,Math.max(0,(Number(pet.dims[dimKey])||0)+amt)); savePet(pet); renderPetCard(); return true; } function addPetExp(amt){ const pet=getPet(); if(!pet)return; pet.exp=(pet.exp||0)+amt; let lvlU=false; while(pet.exp>=100){pet.exp-=100;pet.level=(pet.level||1)+1;lvlU=true} savePet(pet); // 经验条跳动动画 const bar=document.querySelector('.pet-exp-fill'); if(bar){const pct=pet.exp%100;bar.style.transition='none';bar.style.width='0%';requestAnimationFrame(()=>{requestAnimationFrame(()=>{bar.style.transition='width .4s cubic-bezier(.34,1.56,.64,1)';bar.style.width=pct+'%'})})} if(lvlU){showToast('🎉 '+t('petLevelUp').replace('{n}',pet.level));burstConfetti(document.querySelector('.pet-card'))} } // ===== AI Chat ===== async function sendChat(){const i=document.getElementById('chatInput');const m=i.value.trim();if(!m)return;aCM('user',m);i.value='';const tid=aCT();try{const r=await api('/api/chat',{method:'POST',body:{message:m,context:chatCtx}});rCT(tid);aCM('assistant',r.code===0?r.reply:'抱歉,AI暂时无法回复')}catch(e){rCT(tid);aCM('assistant','网络异常,请稍后重试')}} function qA(q){document.getElementById('chatInput').value=q;closeToolModal();toggleAIPanel();setTimeout(()=>sendChat(),300)} function aCM(role,text){const c=document.getElementById('aiMessages');const d=document.createElement('div');d.className='chat-msg '+role;d.innerHTML='
'+esc(text)+'
';c.appendChild(d);c.scrollTop=c.scrollHeight} function aCT(){const c=document.getElementById('aiMessages');const d=document.createElement('div');d.className='chat-msg assistant';d.innerHTML='
';c.appendChild(d);c.scrollTop=c.scrollHeight;return d} function rCT(el){if(el&&el.parentNode)el.remove()} // ===== Utils ===== function esc(t){if(!t)return'';esc._d=esc._d||document.createElement('div');esc._d.textContent=t;return esc._d.innerHTML} function adj(hex,amt){const n=parseInt(hex.replace('#',''),16);const r=Math.min(255,(n>>16)+amt);const g=Math.min(255,((n>>8)&0xFF)+amt);const b=Math.min(255,(n&0xFF)+amt);return '#'+((r<<16)|(g<<8)|b).toString(16).padStart(6,'0')} // ===== Float Hub ===== function toggleHub(){document.getElementById('floatHub').classList.toggle('open')} function closeHub(){document.getElementById('floatHub').classList.remove('open')} document.addEventListener('click',e=>{if(!e.target.closest('.float-hub'))document.getElementById('floatHub').classList.remove('open')}); // ===== Card System ===== function initCardState(){ const raw=localStorage.getItem('hulu_card_state'); if(raw){try{cardState=JSON.parse(raw)}catch(e){}} if(!cardState.owned)cardState.owned=[]; if(!cardState.draws&&cardState.draws!==0)cardState.draws=0; if(!cardState.lastDaily)cardState.lastDaily=''; if(!cardState.knowledgeRead&&cardState.knowledgeRead!==0)cardState.knowledgeRead=0; if(!cardState.drawHistory)cardState.drawHistory=[]; // Daily login const today=new Date().toISOString().slice(0,10); if(cardState.lastDaily!==today){ cardState.draws+=1; cardState.lastDaily=today; saveCardState(); } } function saveCardState(){localStorage.setItem('hulu_card_state',JSON.stringify(cardState));rCG();} function ownedCount(){return cardState.owned.length} function grantCard(code,method){ if(!code)return; if(cardState.owned.indexOf(code)===-1){ cardState.owned.push(code); cardState.drawHistory.push({code:code,date:new Date().toISOString().slice(0,10),method:method}); saveCardState(); return true; } return false; } async function loadCardData(){ if(cardData)return cardData; try{ const r=await api('/api/math_personality'); if(r.code===0&&r.data&&r.data.results){cardData=r.data;cardCache=r.data;return cardData} }catch(e){} return null; } function getCardInfo(code){ if(!cardCache||!cardCache.results)return null; return cardCache.results[code]||null; } function getAllCardCodes(){ if(!cardCache||!cardCache.results)return[]; return Object.keys(cardCache.results); } function getClanInfo(clanId){ if(!cardCache||!cardCache.clans)return null; return cardCache.clans[clanId]||null; } const STAT5_LABELS=['眼光','人文','侦探','建模','联结']; function stats5HTML(info){ const s=(info&&info.stats5)||{}; return '
'+STAT5_LABELS.map(k=>{ const v=Math.min(100,Math.max(0,Number(s[k])||0)); return '
'+k+'
'+(s[k]||0)+'
'; }).join('')+'
'; } function portraitHTML(info,size){ if(!info)return'?'; const fs=size||24; if(info.portrait)return ''+(info.icon||'?')+''; return info.icon||'?'; } // Render card gallery on home page function rCG(){ const wrap=document.getElementById('homeCardGallery'); const btn=document.getElementById('homeGachaBtn'); const cnt=document.getElementById('cardCountText'); const scroll=document.getElementById('cardGalleryScroll'); if(!wrap||!scroll)return; const owned=cardState.owned; if(cnt)cnt.textContent=owned.length+'/16'; if(owned.length===0){ // 还没测过 MathBTI if(!getPet()){ scroll.innerHTML=''; }else{ scroll.innerHTML=''; } wrap.style.display='block'; // 即使没有卡片,有抽卡次数也要显示按钮 if(btn){ if(cardState.draws>0){ btn.style.display='inline-flex'; document.getElementById('homeDrawCount').textContent=cardState.draws; }else{ btn.style.display='none'; } } return; } wrap.style.display='block'; loadCardData().then(()=>{ let h=''; owned.forEach(code=>{ const info=getCardInfo(code); const clan=info?getClanInfo(info.clan):null; const clanColor=clan?clan.color:'#A8907E'; h+=''; }); scroll.innerHTML=h; }); // 抽卡按钮 if(btn){ if(cardState.draws>0){ btn.style.display='inline-flex'; document.getElementById('homeDrawCount').textContent=cardState.draws; }else{ btn.style.display='none'; } } } // Gacha draw function openGacha(){ document.getElementById('gachaOverlay').style.display='flex'; document.getElementById('gachaIdle').style.display='block'; document.getElementById('gachaResult').style.display='none'; document.getElementById('gachaDrawCount').textContent=cardState.draws; const all=cardData?Object.keys(cardData.results).length:16; const missing=all-cardState.owned.length; let pityHint=''; if(cardState.drawHistory.length>0){ const sinceNew=cardState.drawHistory.slice().reverse().findIndex(d=>cardState.owned.indexOf(d.code)===-1); if(sinceNew>=4)pityHint='⭐ 下次抽卡必出新卡!'; else if(sinceNew>=0)pityHint='再抽'+(4-sinceNew)+'次保底出新卡'; } document.getElementById('gachaPityHint').textContent=pityHint||'剩余未收集:'+missing+'/16'; } function closeGacha(){document.getElementById('gachaOverlay').style.display='none'} function doDraw(){ if(_gachaBusy)return; if(cardState.draws<=0){showToast('没有抽卡次数了!去读知识卡片获取吧');return} _gachaBusy=true; const gourd=document.getElementById('gachaGourd'); gourd.classList.add('gourd-shaking'); document.getElementById('gachaIdle').style.display='block'; document.getElementById('gachaResult').style.display='none'; setTimeout(()=>{ gourd.classList.remove('gourd-shaking'); cardState.draws--; saveCardState(); // Pity: every 5th draw guaranteed new let code; const allCodes=getAllCardCodes(); const sinceNewIndex=cardState.drawHistory.slice().reverse().findIndex(d=>!cardState.owned.includes(d.code)); if(sinceNewIndex>=4||(cardState.drawHistory.length>0&&sinceNewIndex===-1)){ // 保底:一定出新卡 const missing2=allCodes.filter(c=>!cardState.owned.includes(c)); code=missing2[Math.floor(Math.random()*missing2.length)]; }else{ code=allCodes[Math.floor(Math.random()*allCodes.length)]; } const isNew=cardState.owned.indexOf(code)===-1; if(isNew){ cardState.owned.push(code); } cardState.drawHistory.push({code:code,date:new Date().toISOString().slice(0,10),method:'gacha'}); saveCardState(); // Show result showDrawResult(code,isNew); // 粒子爆发 const cr=document.querySelector('.gacha-card-reveal'); if(cr)setTimeout(()=>burstGachaConfetti(cr),300); _gachaBusy=false; rCG(); },900); } function showDrawResult(code,isNew){ loadCardData().then(()=>{ const info=getCardInfo(code); const clan=info?getClanInfo(info.clan):null; const clanColor=clan?clan.color:'#A8907E'; document.getElementById('gachaIdle').style.display='none'; const res=document.getElementById('gachaResult'); res.style.display='block'; res.innerHTML= '
'+ (info&&info.portrait ?'
'+(info.icon||'?')+'
' :'
'+(info?info.icon:'?')+'
')+ '
'+(info?info.name:'???')+'
'+ '
'+code+'
'+ (clan?'
'+clan.emoji+' '+clan.name+' · '+(info?info.mathematician:'')+'
':'')+ '
'+(info?info.tagline:'')+'
'+ (info?stats5HTML(info):'')+ (isNew?'':'
(重复获得 · 已自动分解)
')+ ''+ '
'+ '
'; document.getElementById('gachaDrawCount').textContent=cardState.draws; }); } // Card detail modal function openCardDetail(code){ loadCardData().then(()=>{ const info=getCardInfo(code); if(!info)return; const clan=getClanInfo(info.clan); const clanColor=clan?clan.color:'#A8907E'; const hist=cardState.drawHistory.filter(d=>d.code===code); const obtainInfo=hist.length>0 ?(hist[0].method==='mathbti'?'📋 MathBTI测试获得':'🏮 葫芦抽卡获得')+' · '+hist[0].date :'未知途径'; const modal=document.getElementById('cardDetailModal'); modal.innerHTML= '
'+ '
'+ ''+ (info.portrait ?'
'+(info.icon||'?')+'
' :'
'+(info.icon||'?')+'
')+ '
'+esc(info.name)+'
'+ '
'+code+'
'+ (clan?'
'+clan.emoji+' '+clan.name+' · '+esc(info.mathematician)+'
':'')+ '
'+esc(info.tagline||'')+'
'+ '
'+ '
'+ '
人物简介

'+esc(info.description||'')+'

'+ '
五维能力
'+stats5HTML(info)+'
'+ '
代表数学家 · '+esc(info.mathematician)+'

'+esc(info.bio||'')+'

'+ '
💡'+esc(info.bio_fact||'')+'
'+ '
🎴'+obtainInfo+'
'+ '
'; document.getElementById('cardDetailOverlay').style.display='flex'; }); } function closeCardDetail(){document.getElementById('cardDetailOverlay').style.display='none'} function burstGachaConfetti(el){ if(!el)return;const r=el.getBoundingClientRect();const cx=r.left+r.width/2,cy=r.top+r.height/2; const colors=['#A8907E','#C47F3A','#D4A574','#8A9BA8','#C49595','#8AA68A']; for(let i=0;i<18;i++){const p=document.createElement('div');p.className='gacha-particle';const a=Math.random()*Math.PI*2,d=40+Math.random()*60;p.style.cssText='left:'+cx+'px;top:'+cy+'px;--gpx:'+(Math.cos(a)*d)+'px;--gpy:'+(Math.sin(a)*d)+'px;background:'+colors[Math.floor(Math.random()*colors.length)];document.body.appendChild(p);setTimeout(()=>p.remove(),1100)} } // ===== Toast ===== function showToast(msg){ let c=document.getElementById('toastBox');if(!c){c=document.createElement('div');c.id='toastBox';c.className='toast-container';document.body.appendChild(c)} const t=document.createElement('div');t.className='toast';t.innerHTML=msg;c.appendChild(t);setTimeout(()=>t.remove(),2400); } // ===== 粒子爆发 ===== function burstConfetti(el){ if(!el)return;const r=el.getBoundingClientRect();const cx=r.left+r.width/2,cy=r.top+r.height/2; const colors=['#808C95','#A8907E','#8AA68A','#C4A467','#C49595','#9AA5AE']; for(let i=0;i<14;i++){const p=document.createElement('div');p.className='particle';const a=Math.random()*Math.PI*2,d=30+Math.random()*50;p.style.cssText='left:'+cx+'px;top:'+cy+'px;width:6px;height:6px;border-radius:2px;background:'+colors[Math.floor(Math.random()*colors.length)]+';--px:'+(Math.cos(a)*d)+'px;--py:'+(Math.sin(a)*d)+'px';document.body.appendChild(p);setTimeout(()=>p.remove(),750)} } // ===== 骨架屏 ===== function renderSkeletons(cid,count){ const c=document.getElementById(cid);c.innerHTML=''; for(let i=0;i
')} } // ===== Init ===== let favP=null; document.addEventListener('DOMContentLoaded',()=>{ // 语言初始化 document.querySelectorAll('.lang-btn').forEach(b=>b.classList.toggle('active',b.dataset.lang===_lang)); document.getElementById('globalSearch').placeholder=t('search'); rI18n(); // 首屏:首页纯本地渲染,立即呈现;数据请求并行后台预取 loadHome(); initCardState(); loadCardData(); setTimeout(rCG,300); favP=rFC(); api('/api/videos?module=数人').then(r=>{if(r.code===0){allV=r.data||[];vD=r.disciplines||[]}}).catch(()=>{}); api('/api/knowledge').then(r=>{if(r.code===0){allK=r.data||[];kT=r.topics||[]}}).catch(()=>{}); api('/api/user/profile?user_id='+USER_ID).then(r=>{if(r.code===0&&r.data){document.getElementById('sidebarName').textContent=r.data.nickname||'游客用户';document.getElementById('sidebarAvatar').textContent=r.data.avatar_emoji||'葫';document.getElementById('sidebarAvatar').style.background=r.data.avatar_color||'#808C95'}}).catch(()=>{}); }); // ===== Math Life ===== let mtD=null,mtS={currentNode:null,stats:{shuli:10,xiayi:10,xinzhi:10},fav:{}}; const MT_STAGE_MAP={c1:'第一幕 · 大一上:小镇少年走进数学系',c2:'第二幕 · 大一下:第一次完整证明',c3:'第三幕 · 大二上:讨论班与诺特',c4:'第四幕 · 大二下:数学与生活',c5:'第五幕 · 大三上:方向与高斯',c6:'第六幕 · 大三下:科研低谷与诺特',c7:'第七幕 · 大四上:直博申请',c8:'第八幕 · 毕业前夜与直博第一年'}; let alumniList=[],alumniStory=null,alumniStoryIndex=0; function mtStage(id){const k=(id||'').slice(0,2);return MT_STAGE_MAP[k]||'数学少年线'} function mtStatColor(k){if(k==='shuli')return '#3F6C8F';if(k==='xinzhi')return '#2E7D72';return '#B8893A'} function mtStatLabel(k){if(k==='shuli')return '数理根基';if(k==='xinzhi')return '心志韧性';return '同行温度'} function mtStatIcon(k){if(k==='shuli')return '∑';if(k==='xinzhi')return 'θ';return 'π'} function mtEffectsHint(ef){if(!ef)return '';const p=[];if(ef.shuli)p.push('数理根基 '+(ef.shuli>0?'+':'')+ef.shuli);if(ef.xiayi)p.push('同行温度 '+(ef.xiayi>0?'+':'')+ef.xiayi);if(ef.xinzhi)p.push('心志韧性 '+(ef.xinzhi>0?'+':'')+ef.xinzhi);if(ef.favorability){for(const k in ef.favorability)p.push(k+'好感 '+(ef.favorability[k]>0?'+':'')+ef.favorability[k])}return p.length?'['+p.join(' | ')+']':''} function loadMathLife(){ chatCtx={}; const hub=document.getElementById('mathLifeHub'),teen=document.getElementById('mathTeenPlayer'),legacy=document.getElementById('legacySimPlayer'),alumni=document.getElementById('mathAlumniPlayer'); if(hub)hub.style.display=''; if(teen)teen.style.display='none'; if(legacy)legacy.style.display='none'; if(alumni)alumni.style.display='none'; const themes=[ {icon:'🔱',name:'信仰者',desc:'数学少年线 · 高斯 / 祖冲之 / 欧拉 / 诺特',open:'openMathTeen()',ready:true}, {icon:'📣',name:'传播者',desc:'斐波那契 / 华罗庚 / 伽罗瓦 / Ada Lovelace',open:'',ready:false}, {icon:'🛠️',name:'应用者',desc:'秦九韶 / 牛顿 / 图灵 / 冯·诺依曼',open:'',ready:false}, {icon:'👁️',name:'直觉者',desc:'希帕提娅 / 赵爽 / 庞加莱 / 凯瑟琳·约翰逊',open:'',ready:false} ]; hub.innerHTML= '
四大主题
'+ '
'+ themes.map(t=>'
'+ '
'+t.icon+'
'+ '
'+esc(t.name)+'
'+ '
'+esc(t.desc)+'
'+ (t.ready?'':'
敬请期待
')+ '
').join('')+ '
'+ ''+ ''+ '
'+ '
更多数学人生 · 敬请期待
'+ '
更多主题正在创作中,敬请期待
'+ '
'+ [ {icon:'📣',name:'传播者',desc:'斐波那契 / 华罗庚 / 伽罗瓦 / Ada Lovelace'}, {icon:'🛠️',name:'应用者',desc:'秦九韶 / 牛顿 / 图灵 / 冯·诺依曼'}, {icon:'👁️',name:'直觉者',desc:'希帕提娅 / 赵爽 / 庞加莱 / 凯瑟琳·约翰逊'} ].map(m=>'
'+m.icon+'
'+esc(m.name)+'
'+esc(m.desc)+'
敬请期待
').join('')+ '
'+ '
'; } async function openMathTeen(){ const hub=document.getElementById('mathLifeHub'),teen=document.getElementById('mathTeenPlayer'),legacy=document.getElementById('legacySimPlayer'); if(hub)hub.style.display='none'; if(legacy)legacy.style.display='none'; if(teen){teen.style.display='';teen.innerHTML='
🧑‍🎓 数学少年线
正在加载剧本…
'} try{const r=await api('/api/story/math_teen');if(r.code===0){mtD=r.data;const saved=localStorage.getItem('mb_math_teen_save');if(saved){try{mtS=JSON.parse(saved)}catch(e){mtS={currentNode:null,stats:{shuli:10,xiayi:10,xinzhi:10},fav:{}}}}const sn=mtD.start_node||'c1_notice';const ni=(mtS.currentNode&&mtD.nodes&&mtD.nodes[mtS.currentNode])?mtS.currentNode:sn;mtS.currentNode=ni;rMTNode(ni)}else throw new Error('load failed')}catch(e){const b=document.getElementById('mtStoryBox');if(b)b.innerHTML='

数学少年线加载失败

'}} function rMTNode(ni){ if(!mtD||!mtD.nodes||!mtD.nodes[ni]){document.getElementById('mtStoryBox').innerHTML='

节点未找到

';return} const n=mtD.nodes[ni];mtS.currentNode=ni;localStorage.setItem('mb_math_teen_save',JSON.stringify(mtS)); const st=document.getElementById('mtStage');if(st)st.textContent=mtStage(ni); const stats=document.getElementById('mtStats');if(stats)stats.innerHTML= ['shuli','xiayi','xinzhi'].map(k=>'
'+mtStatIcon(k)+'
'+(mtS.stats[k]||0)+''+mtStatLabel(k)+'
').join('')+ '
4
4 位数学家高斯 · 祖冲之 · 欧拉 · 诺特
'+ ''; const box=document.getElementById('mtStoryBox');if(box)box.innerHTML=(n.character?'
'+esc(n.character)+'
':'')+'
'+esc(n.scene||'')+'
'; rMTChoices(n.choices||[]); } function rMTChoices(choices){ const area=document.getElementById('mtChoices');if(!area)return; if(!choices.length){area.innerHTML='
故事结束 · 你的数学,刚刚开始
';const rb=document.getElementById('mtRestartBtn');if(rb)rb.addEventListener('click',resetMathTeen);return} area.innerHTML=choices.map((c,i)=>'').join(''); area.querySelectorAll('.story-choice').forEach(btn=>{ const c=choices[Number(btn.getAttribute('data-mt-index'))]; btn.addEventListener('click',()=>mTC(c.next,c.effects)); }); } function mTC(next,ef){if(ef){if(ef.shuli)mtS.stats.shuli=(mtS.stats.shuli||10)+ef.shuli;if(ef.xiayi)mtS.stats.xiayi=(mtS.stats.xiayi||10)+ef.xiayi;if(ef.xinzhi)mtS.stats.xinzhi=(mtS.stats.xinzhi||10)+ef.xinzhi;if(ef.favorability){if(!mtS.fav)mtS.fav={};for(const k in ef.favorability)mtS.fav[k]=(mtS.fav[k]||0)+ef.favorability[k]}}rMTNode(next);addPetExp(5)} function resetMathTeen(){localStorage.removeItem('mb_math_teen_save');mtS={currentNode:null,stats:{shuli:10,xiayi:10,xinzhi:10},fav:{}};if(mtD&&mtD.start_node)rMTNode(mtD.start_node);else openMathTeen()} function backToMathLife(){loadMathLife()} async function openMathAlumni(){ const hub=document.getElementById('mathLifeHub'),teen=document.getElementById('mathTeenPlayer'),legacy=document.getElementById('legacySimPlayer'),alumni=document.getElementById('mathAlumniPlayer'); if(hub)hub.style.display='none'; if(teen)teen.style.display='none'; if(legacy)legacy.style.display='none'; if(!alumni)return; alumni.style.display=''; alumni.innerHTML='
数学校友访谈
真实校友的数学人生 · Skill 剧本包
校友卡片
点进一位校友,线性体验他/她的大学四年
'; loadAlumniCards(); } async function loadAlumniCards(){ try{const r=await api('/api/alumni');alumniList=(r&&r.code===0&&Array.isArray(r.data))?r.data:[]}catch(e){alumniList=[]} const box=document.getElementById('alumniCards');if(!box)return; const cards=alumniList.length ?alumniList.map(alumniCardHTML).join('') :[1,2,3,4,5,6].map(i=>'').join(''); box.innerHTML=cards+''; } function alumniCardHTML(a){ const skill=a.skill||{},tags=(skill.tags&&skill.tags.length)?skill.tags:['数学人生']; return ''; } function openAlumniStory(id){ alumniStory=alumniList.find(a=>a.id===id)||null; alumniStoryIndex=0; const list=document.getElementById('alumniListView'),story=document.getElementById('alumniStoryView'); if(list)list.style.display='none'; if(story){ story.style.display=''; story.innerHTML='
'+esc(alumniStory?alumniStory.name:'校友剧本')+'
'+esc(alumniStory?(alumniStory.career||'大学四年的数学人生'):'剧本整理中')+'
'; renderAlumniStory(); } } function backToAlumniList(){ const list=document.getElementById('alumniListView'),story=document.getElementById('alumniStoryView'); if(list)list.style.display=''; if(story)story.style.display='none'; } function renderAlumniStory(){ const body=document.getElementById('alumniStoryBody');if(!body)return; const nodes=alumniStory&&alumniStory.four_years&&alumniStory.four_years.nodes?alumniStory.four_years.nodes:[]; if(!nodes.length){ body.innerHTML='
该校友的 Skill 剧本正在整理中。
'+['大一','大二','大三','大四'].map((s,i)=>'
'+(i+1)+''+s+'剧情节点整理中
').join('')+'
'; return; } const n=nodes[Math.min(alumniStoryIndex,nodes.length-1)]; const pct=Math.round((alumniStoryIndex+1)/nodes.length*100); body.innerHTML='
'+esc(n.stage||('第 '+(alumniStoryIndex+1)+' 幕'))+'
'+esc(n.scene||'')+'
'+(n.effect?'
'+fiveDimEffectHTML(n.effect)+'
':'')+ (alumniStoryIndex下一幕 →' :'
'+esc(n.ending||'这就是这位校友的数学人生。')+'
')+ '
'; } function nextAlumniNode(){alumniStoryIndex++;renderAlumniStory()} function fiveDimEffectHTML(ef){ if(!ef)return''; const labels={眼光:'数学眼光',人文:'数学人文',侦探:'数学侦探',建模:'数学建模',联结:'数学联结'}; const arr=Object.keys(ef||{}).filter(k=>labels[k]&&ef[k]); if(!arr.length)return''; return '五维能力关联:'+arr.map(k=>labels[k]+' '+(ef[k]>0?'+':'')+ef[k]).join(' · ')+''; } function openLegacySim(){const hub=document.getElementById('mathLifeHub'),teen=document.getElementById('mathTeenPlayer'),legacy=document.getElementById('legacySimPlayer');if(hub)hub.style.display='none';if(teen)teen.style.display='none';if(legacy)legacy.style.display='';loadLegacySimulator()}