Files
pk10/Static/js/pk10-poll.js
T
li 4a94fe36cf feat: 重构代码架构 + 新增报表/跟单/输赢统计功能
- 拆分 HomeController → TransactionController, ReportWebController, FollowPlanController
- 新增 Service 层: TransactionService, ReportService, FollowPlanService
- pk10.php JS 抽离为 4 个独立文件 (sound/race/bet/poll)
- 前台新增报表查询页面 (/report) + 跟单计划页面 (/follow-plan)
- 后台新增跟单计划管理 + 用户输赢明细统计
- 封盘状态显示倒计时 (x:xx)
- 音效仅在开奖弹窗打开时播放
- 路由按模块分组整理
- autoload 支持 App\Services 命名空间
2026-03-27 18:41:06 +08:00

128 lines
7.2 KiB
JavaScript

/**
* PK10 Polling & Countdown
* Depends on: pk10-sound.js, pk10-race.js, pk10-bet.js
* Globals expected: GAME_ID, periodData, historyCache, I18N, localCountdown,
* localLockCountdown, lastPollStatus, lastPeriodId, drawnResultShown,
* cachedRaceResult, raceTriggered, pendingRaceHeader, raceAnimState,
* PK10_CONFIG (for status labels)
*/
// ===== Countdown interval =====
setInterval(function(){if(localCountdown>0)localCountdown--;if(localLockCountdown>0)localLockCountdown--;var m=Math.floor(localCountdown/60),s=localCountdown%60;document.getElementById('countdown').textContent=localCountdown>0?m+':'+(s<10?'0':'')+s:'--';
var badge=document.getElementById('statusBadge');
if(lastPollStatus==='pending'&&localLockCountdown>0){var lm=Math.floor(localLockCountdown/60),ls=localLockCountdown%60;badge.textContent=lm+':'+(ls<10?'0':'')+ls;badge.style.cssText='color:var(--success)';}
else if(lastPollStatus==='pending'&&localLockCountdown<=0){badge.textContent=PK10_CONFIG.lang.sealed;badge.style.cssText='color:var(--danger)';}
if(raceAnimState==='waiting')updateRaceOverlayTimer(localCountdown);
if(localCountdown>0&&localCountdown<=10&&raceAnimState==='waiting')SoundFX.tick();
},1000);
// ===== Trigger race sequence =====
function triggerRaceSequence(){
if(raceTriggered||raceAnimState!=='waiting')return;
raceTriggered=true;
var remaining=localCountdown*1000;
var readyDur=3000;
var raceDur=Math.max(5500,remaining-readyDur-500);
raceDur=Math.min(raceDur,25000);
var waitBeforeRace=Math.max(0,remaining-readyDur-raceDur);
startReadyAnimation();
setTimeout(function(){
if(cachedRaceResult&&raceAnimState==='ready'){
var raceRes=cachedRaceResult;cachedRaceResult=null;
pendingRaceHeader=raceRes._raw||null;
animateRace(raceRes.arr,raceDur);
}
},readyDur+waitBeforeRace);
}
// ===== Poll period =====
function pollPeriod(){return fetch('/api/period/current?game_id='+GAME_ID).then(function(r){return r.json();}).then(function(d){if(!d.success)return;periodData=d.data;
var serverRemaining=periodData.remaining_seconds||0;var periodChanged=(periodData.id&&periodData.id!==lastPeriodId);var statusChanged=(periodData.status!==lastPollStatus);var driftTooMuch=Math.abs(localCountdown-serverRemaining)>3;
if(periodChanged||statusChanged||driftTooMuch||localCountdown<=0)localCountdown=serverRemaining;
var serverLockCountdown=periodData.lock_countdown||0;if(periodChanged||statusChanged||Math.abs(localLockCountdown-serverLockCountdown)>3)localLockCountdown=serverLockCountdown;
if(d.balance!==null&&d.balance!==undefined)document.getElementById('balanceDisplay').textContent=parseFloat(d.balance).toFixed(2);
// Status badge
var badge=document.getElementById('statusBadge');var statusMap={pending:[PK10_CONFIG.lang.betting,'seal-status','color:var(--success)'],locked:[PK10_CONFIG.lang.sealed,'seal-status','color:var(--danger)'],drawn:[I18N.drawing,'seal-status','color:var(--warn)'],settled:[I18N.settled_label,'seal-status','color:var(--warn)']};var s=statusMap[periodData.status]||statusMap.pending;badge.textContent=s[0];badge.className=s[1];badge.style.cssText=s[2];
switchVideoOrAnim(periodData.status,statusChanged);
// Period number
var fullPn=periodData.period_number||'---';var shortPn=fullPn.length>8?fullPn.slice(0,-4)+'-'+fullPn.slice(-4):fullPn;var pnEl=document.getElementById('periodNum');pnEl.textContent=shortPn;pnEl.title=fullPn;
// Extract result array
function extractResultArr(res){if(!res)return null;var arr=[res.rank_1,res.rank_2,res.rank_3,res.rank_4,res.rank_5,res.rank_6,res.rank_7,res.rank_8,res.rank_9,res.rank_10];return arr.every(function(v){return v;})?arr:null;}
// Scene header sync
if(raceAnimState!=='racing'&&raceAnimState!=='finished'&&raceAnimState!=='ready'){
if(d.last_result) updateSceneHeader(d.last_result, d.last_result.period_number||fullPn);
}
// === Pending → idle ===
if(periodData.status==='pending'){
cachedRaceResult=null;raceTriggered=false;
if(d.last_result){
var lp=d.last_result.period_number||'';
document.getElementById('lastPeriodNum').textContent=lp.length>4?'#'+lp.slice(-4):lp;
renderResult(d.last_result,'lastResult');
}
if(raceAnimState==='finished'||raceAnimState==='racing'||raceAnimState==='ready'){
if(!window._podiumTimer){
var protectMs=8000;
if(raceAnimState==='ready')protectMs=16000;
else if(raceAnimState==='racing')protectMs=12000;
window._podiumTimer=setTimeout(function(){
resetRace();
startIdleAnimation();
window._podiumTimer=null;
},protectMs);
}
} else if(raceAnimState!=='waiting'){
resetRace();
startIdleAnimation();
}
}
// === Locked → cache result + trigger sprint ===
if(periodData.status==='locked'){
var raceRes=extractResultArr(d.race_result);
if(raceRes&&!cachedRaceResult){
cachedRaceResult={arr:raceRes,_raw:d.race_result};
}
if(cachedRaceResult)triggerRaceSequence();
}
// === Drawn/Settled → show result + podium ===
if(d.last_result&&(periodData.status==='drawn'||periodData.status==='settled')){
if(statusChanged||periodChanged){
var lp=d.last_result.period_number||'';
document.getElementById('lastPeriodNum').textContent=lp.length>4?'#'+lp.slice(-4):lp;
var ra=extractResultArr(d.last_result);
if(ra&&raceAnimState!=='finished'&&raceAnimState!=='racing'&&raceAnimState!=='ready'){
cachedRaceResult=null;
pendingRaceHeader=d.last_result;
animateRace(ra);
} else if(raceAnimState!=='racing'&&raceAnimState!=='ready'){
updateSceneHeader(d.last_result, lp);
}
setTimeout(function(){renderResult(d.last_result,'lastResult');},5500);
}
}
// === First load ===
if(d.last_result&&!lastAnimatedResult&&raceAnimState==='idle'){
var ra=extractResultArr(d.last_result);
if(ra){
var lp=d.last_result.period_number||'';
document.getElementById('lastPeriodNum').textContent=lp.length>4?'#'+lp.slice(-4):lp;
renderResult(d.last_result,'lastResult');
startIdleAnimation();
}
}
renderMyBets(d.my_bets||[]);renderLastMyBets(d.last_my_bets||[]);
// History
if(d.history&&periodChanged){historyCache=d.history;var hl=document.getElementById('historyList');hl.innerHTML='';d.history.forEach(function(h){var row=document.createElement('div');row.className='card';row.style.cssText='padding:6px 8px;margin-bottom:4px';var pn=h.period_number||'';var sp=pn.length>4?'#'+pn.slice(-4):pn;var balls='';['rank_1','rank_2','rank_3','rank_4','rank_5','rank_6','rank_7','rank_8','rank_9','rank_10'].forEach(function(k){if(h[k])balls+='<div class="car c'+h[k]+'" style="width:18px;height:18px;font-size:8px;flex-shrink:0">'+h[k]+'</div>';});row.innerHTML='<div style="color:var(--text3);font-size:10px;margin-bottom:2px">'+sp+'</div><div style="display:flex;gap:2px;flex-wrap:wrap">'+balls+'</div>';hl.appendChild(row);});if(!document.getElementById('panelDragon').classList.contains('hidden'))renderDragonStreak();}
lastPollStatus=periodData.status;lastPeriodId=periodData.id||0;
}).catch(function(e){console.error('pollPeriod error:',e);});}
// ===== Start polling =====
pollPeriod();
(function dynamicPoll(){var delay=(lastPollStatus==='locked'||lastPollStatus==='drawn'||lastPollStatus==='settled')?1500:3000;setTimeout(function(){pollPeriod().then(dynamicPoll).catch(dynamicPoll);},delay);})();