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 命名空间
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* PK10 Sound Engine (SoundFX)
|
||||
* Depends on: (none - first to load)
|
||||
* Provides: SoundFX, toggleRaceSound
|
||||
*
|
||||
* Sound only plays when the race modal is open (raceModalOpen === true).
|
||||
* betSuccess/error are exempt — they are UI feedback, not race sounds.
|
||||
*/
|
||||
var SoundFX={
|
||||
enabled:localStorage.getItem('raceSoundOn')!=='0',
|
||||
_cache:{},
|
||||
// Core play — checks both enabled AND raceModalOpen
|
||||
_play:function(name,vol,startAt){if(!this.enabled||!window.raceModalOpen)return null;var a=new Audio('/Static/sounds/'+name+'.mp3');a.volume=Math.min(vol||0.6,1);if(startAt)a.currentTime=startAt;a.play().catch(function(){});return a;},
|
||||
_stop:function(key){if(this._cache[key]){try{this._cache[key].pause();this._cache[key].currentTime=0;}catch(e){}delete this._cache[key];}},
|
||||
engineIdle:function(){this._cache.idle=this._play('engine',0.3);},
|
||||
trafficBeep:function(n){this._play('beep',n>0?0.4:0.6);},
|
||||
engineStart:function(){this._stop('idle');this._cache.eng=this._play('engine',0.7);},
|
||||
accelerate:function(){this._stop('eng');this._cache.race=this._play('racing',0.5);},
|
||||
gearShift:function(){this._play('engine',0.25,1.5);},
|
||||
whoosh:function(){this._play('whoosh',0.5);},
|
||||
brakeSqueal:function(){this._stop('race');this._play('brake',0.6);},
|
||||
finish:function(){this._play('finish',0.7);},
|
||||
podium:function(){this._play('finish',0.5);},
|
||||
// tick — race countdown sound, only when modal open
|
||||
tick:function(){if(!this.enabled||!window.raceModalOpen)return;try{var ctx=new(window.AudioContext||window.webkitAudioContext)();var o=ctx.createOscillator(),g=ctx.createGain();o.type='sine';o.frequency.value=800;g.gain.setValueAtTime(0.1,ctx.currentTime);g.gain.linearRampToValueAtTime(0,ctx.currentTime+0.05);o.connect(g).connect(ctx.destination);o.start();o.stop(ctx.currentTime+0.05);}catch(e){}},
|
||||
// betSuccess/error — UI feedback, always plays when enabled (no modal check)
|
||||
betSuccess:function(){if(!this.enabled)return;try{var ctx=new(window.AudioContext||window.webkitAudioContext)();[880,1175].forEach(function(f,i){var o=ctx.createOscillator(),g=ctx.createGain();o.type='sine';o.frequency.value=f;g.gain.setValueAtTime(0.08,ctx.currentTime+i*0.08);g.gain.linearRampToValueAtTime(0,ctx.currentTime+i*0.08+0.12);o.connect(g).connect(ctx.destination);o.start(ctx.currentTime+i*0.08);o.stop(ctx.currentTime+i*0.08+0.12);});}catch(e){}},
|
||||
error:function(){if(!this.enabled)return;try{var ctx=new(window.AudioContext||window.webkitAudioContext)();var o=ctx.createOscillator(),g=ctx.createGain();o.type='square';o.frequency.setValueAtTime(200,ctx.currentTime);o.frequency.setValueAtTime(150,ctx.currentTime+0.15);g.gain.setValueAtTime(0.06,ctx.currentTime);g.gain.linearRampToValueAtTime(0,ctx.currentTime+0.3);o.connect(g).connect(ctx.destination);o.start();o.stop(ctx.currentTime+0.3);}catch(e){}},
|
||||
// stopAll — kill all cached sounds (called when modal closes)
|
||||
stopAll:function(){for(var k in this._cache){this._stop(k);}},
|
||||
toggle:function(){this.enabled=!this.enabled;localStorage.setItem('raceSoundOn',this.enabled?'1':'0');return this.enabled;}
|
||||
};
|
||||
function toggleRaceSound(){var on=SoundFX.toggle();var btn=document.querySelector('.rs-sound');if(btn)btn.textContent=on?'🔊':'🔇';if(on)SoundFX.tick();}
|
||||
Reference in New Issue
Block a user