Files
2026-10-28 09:36:55 +08:00

141 lines
8.2 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
const defs=[
{key:'P',title:'红色火星站',color:'#ff3b52',light:'#ff8a9b',dark:'#c40f31',deep:'#4a0416',desc:'火星基地监控台:检查氧气、通讯和火箭准备状态。',facts:['氧气储备 82%','基地温度 -63°C','通讯延迟 3.2 秒']},
{key:'Q',title:'蓝色深海舱',color:'#357bff',light:'#8ab4ff',dark:'#1146d6',deep:'#041348',desc:'深海实验舱控制台:观察压载、声呐和探照灯。',facts:['水深 3820 米','舱内压强 1.0 atm','声呐信号正常']},
{key:'R',title:'绿色能量站',color:'#00d164',light:'#7dffb4',dark:'#009e49',deep:'#02321a',desc:'绿色能源调度台:管理风机、储能和供电负载。',facts:['风机功率 46 MW','储能电量 73%','今日减排 128 吨']},
{key:'T',title:'黄色指挥所',color:'#ffdb00',light:'#fff176',dark:'#ff9800',deep:'#4a2c00',desc:'应急指挥所大屏:核对地图、队伍和命令下达。',facts:['在线队伍 6 组','待处理事件 2 件','指挥频道已加密']},
{key:'Y',title:'紫色观测台',color:'#c159ff',light:'#e4a6ff',dark:'#7c3aed',deep:'#22065a',desc:'星空观测台:记录星图、光谱和观测日志。',facts:['目标星等 8.4','光谱类型 K2','曝光时间 30 秒']},
{key:'U',title:'橙色发射场',color:'#ff7a18',light:'#ffb377',dark:'#e03a00',deep:'#451000',desc:'发射场控制台:确认倒计时、气象和外部通讯。',facts:['倒计时 T-12:00','风速 4.6 m/s','发射窗口 14:40']}
];
const pathPage=Number((location.pathname.match(/\/page\/([1-6])\/?$/)||[])[1]);
const no=pathPage||Number(document.body.dataset.trainerPage)||1;
const def=defs[no-1];
document.body.classList.add('training');
document.body.style.setProperty('--page-color',def.color);
document.body.style.setProperty('--page-light',def.light);
document.body.style.setProperty('--page-dark',def.dark);
document.body.style.setProperty('--page-deep',def.deep);
let themeMeta=document.querySelector('meta[name="theme-color"]');
if(!themeMeta){themeMeta=document.createElement('meta');themeMeta.name='theme-color';document.head.appendChild(themeMeta)}
themeMeta.content=def.dark;
document.body.dataset.trainerPage=String(no);
document.title=`${no}. ${def.title}`;
document.getElementById('pageTitle').textContent=`${no}. ${def.title}`;
document.getElementById('pageHeading').textContent=`${no}. ${def.title}`;
document.getElementById('pageDesc').textContent=def.desc;
const facts=document.getElementById('pageFacts');facts.innerHTML='';def.facts.forEach(text=>{const el=document.createElement('div');el.textContent=text;facts.appendChild(el)});
const mark=document.getElementById('mark');mark.textContent=no;mark.style.background='rgba(255,255,255,.18)';mark.style.boxShadow='inset 0 0 0 2px #ffffff70';
const buttons=['火箭准备','燃料检查','通讯开启','系统诊断','备用电源','外部链接'];
const ribbon=document.getElementById('ribbon');
let ribbonKey='K';
function openLink(item){
window._linkMenu=false;
window._panel=null;
const popup=window.open(item.url,'_blank','noopener');
renderRibbon('B',false);
flash(popup?`正在打开 ${item.name}`:'浏览器拦截了弹窗,请允许本站弹出式窗口');
reportEvent({type:'url',url:item.url,page:no});
}
function openLinkMenu(){
window._panel=null;
window._linkMenu=true;
renderRibbon('B',true);
flash('请按 D 打开百度,或按 F 打开 Bilibili(不必按着 Alt)');
}
function activateButton(index,via='click'){
const name=buttons[index-1];
if(name==='外部链接'){
flash(via==='key'?'已打开外部链接菜单':'已点击:外部链接');
reportEvent({type:'button',page:no,index,name});
openLinkMenu();
return;
}
flash(via==='key'?`快捷键已触发:${index}. ${name}`:`已点击:${name}`);
reportEvent({type:'button',page:no,index,name});
}
function renderRibbon(activeKey=ribbonKey,keyboardOn=!!window._panel||!!window._linkMenu){
ribbonKey=activeKey;
ribbon.className='ribbon'+(keyboardOn?' keyboard-on':'');
ribbon.innerHTML='';
const tabs=document.createElement('div');
tabs.className='ribbon-tabs';
RIBBON_PANELS.forEach(panel=>{
const tab=document.createElement('button');
tab.type='button';
tab.className='ribbon-tab'+(panel.key===activeKey?' active':'')+(window._panel===panel.key||(window._linkMenu&&panel.key==='B')?' armed':'');
tab.innerHTML=`<span class="keytip">Alt+${panel.key}</span><strong>${panel.title}</strong>`;
tab.onclick=()=>{window._panel=null;window._linkMenu=false;renderRibbon(panel.key,false)};
tabs.appendChild(tab);
});
const body=document.createElement('div');
body.className='ribbon-body';
if(window._linkMenu){
const pane=document.createElement('div');
pane.className='ribbon-panel active';
const title=document.createElement('div');
title.className='ribbon-subhint';
title.textContent='外部链接:按 D 打开百度,按 F 打开 Bilibili';
body.appendChild(title);
LINK_TARGETS.forEach(item=>{
const b=document.createElement('button');
b.type='button';
b.className='ribbon-cmd '+(item.key==='A'?'success':'ghost');
b.innerHTML=`<span class="keytip">${item.key}</span><span>${item.name}</span>`;
b.onclick=()=>openLink(item);
pane.appendChild(b);
});
body.appendChild(pane);
}else{
RIBBON_PANELS.forEach(panel=>{
const pane=document.createElement('div');
pane.className='ribbon-panel'+(panel.key===activeKey?' active':'');
panel.items.forEach(item=>{
const b=document.createElement('button');
b.type='button';
b.className='ribbon-cmd '+(item.index%2?'success':'ghost');
b.innerHTML=`<span class="keytip">${item.key}</span><span>${item.index}. ${item.name}</span>`;
b.onclick=()=>{window._panel=null;window._linkMenu=false;renderRibbon(panel.key,false);activateButton(item.index,'click')};
pane.appendChild(b);
});
body.appendChild(pane);
});
}
ribbon.appendChild(tabs);
ribbon.appendChild(body);
}
function openRibbonPanel(key){
const panel=RIBBON_PANELS.find(item=>item.key===key);
if(!panel)return;
window._linkMenu=false;
window._panel=key;
renderRibbon(key,true);
const letters=panel.items.map(item=>item.key).join(' 或 ');
flash(`已展开「${panel.title}」面板,请按 ${letters}`);
}
renderRibbon('K',false);
(async()=>{const s=await API.get('/api/student/state');if(s.authenticated){await applyStudentState(s);setInterval(refreshStudent,1000)}else location.href='/'})();
reportEvent({type:'page',page:no});
const ctx=document.getElementById('contextMenu');
document.addEventListener('keydown',e=>{
if(e.repeat)return;
const k=keyName(e);
const menu=!ctx.classList.contains('hidden');
if(menu){const map={E:'refresh',R:'rename',C:'copy'};if(map[k]){consumeKey(e);ctx.classList.add('hidden');flash(`快捷菜单:${map[k]}`);reportEvent({type:'menu',item:map[k],page:no});return}}
if(window._linkMenu){
const item=linkByKey(k);
if(item){consumeKey(e);openLink(item);return}
if(k==='ESCAPE'){consumeKey(e);window._linkMenu=false;renderRibbon('B',false);return}
if(!e.altKey){consumeKey(e);return}
}
if(e.altKey&&!e.ctrlKey&&!e.metaKey&&!e.shiftKey&&/^[1-6]$/.test(k)){consumeKey(e);window._panel=null;window._linkMenu=false;renderRibbon(ribbonKey,false);activateButton(Number(k),'key');return}
if(e.altKey&&!e.ctrlKey&&!e.metaKey&&RIBBON_PANELS.some(panel=>panel.key===k)){consumeKey(e);openRibbonPanel(k);return}
if(window._panel){
const panel=RIBBON_PANELS.find(item=>item.key===window._panel);
const item=panel.items.find(cmd=>cmd.key===k);
if(item){consumeKey(e);window._panel=null;renderRibbon(panel.key,false);activateButton(item.index,'key');return}
if(k==='ESCAPE'){consumeKey(e);window._panel=null;renderRibbon(panel.key,false);return}
}
},true);
document.addEventListener('contextmenu',e=>{e.preventDefault();ctx.style.left=Math.min(e.clientX,innerWidth-210)+'px';ctx.style.top=Math.min(e.clientY,innerHeight-160)+'px';ctx.classList.remove('hidden')});
document.addEventListener('click',e=>{if(!ctx.contains(e.target))ctx.classList.add('hidden');if((window._panel||window._linkMenu)&&ribbon&&!ribbon.contains(e.target)){window._panel=null;window._linkMenu=false;renderRibbon(ribbonKey,false)}});
document.querySelectorAll('[data-menu]').forEach(b=>b.onclick=async()=>{ctx.classList.add('hidden');flash('快捷菜单:'+b.dataset.menu);await reportEvent({type:'menu',item:b.dataset.menu,page:no})});