迁移百家乐主面板脚本

This commit is contained in:
2026-04-25 18:30:29 +08:00
parent 9ba18315cc
commit 0953e03b73
3 changed files with 463 additions and 347 deletions
@@ -9,7 +9,13 @@
--}}
{{-- 百家乐主面板 --}}
<div id="baccarat-panel" x-data="baccaratPanel()" x-show="show" x-cloak>
<div id="baccarat-panel"
x-data="baccaratPanel()"
x-show="show"
x-cloak
data-baccarat-current-url="{{ route('baccarat.current') }}"
data-baccarat-bet-url="{{ route('baccarat.bet') }}"
data-baccarat-history-url="{{ route('baccarat.history') }}">
<div x-transition:enter="transition ease-out duration-300" x-transition:enter-start="opacity-0 scale-95"
x-transition:enter-end="opacity-100 scale-100" x-transition:leave="transition ease-in duration-200"
x-transition:leave-start="opacity-100 scale-100" x-transition:leave-end="opacity-0 scale-95"
@@ -371,350 +377,5 @@
}
</style>
<script>
/**
* 百家乐游戏面板 Alpine 组件
*/
function baccaratPanel() {
return {
show: false,
phase: 'idle', // idle | betting | waiting | settled
roundId: null,
totalSeconds: 60,
countdown: 60,
countdownTimer: null,
// 下注池统计
totalBetBig: 0,
totalBetSmall: 0,
totalBetTriple: 0,
// 押注人数统计
betCountBig: 0,
betCountSmall: 0,
betCountTriple: 0,
// 本人下注
myBet: false,
myBetType: '',
myBetAmount: 0,
// 下注表单
selectedType: '',
betAmount: 100,
minBet: 100,
maxBet: 50000,
submitting: false,
// 结算结果
settledDice: [],
settledTotal: 0,
settledResult: '',
resultLabel: '',
diceEmoji: '',
myWon: false,
myPayout: 0,
// 历史记录
history: [],
// 结算后定时自动关闭
autoCloseTimer: null,
autoCloseCountdown: 0,
/**
* 同步全局聊天上下文中的金币余额,供弹窗右上角与其他面板共用。
*/
syncUserGold(jjb) {
if (jjb === undefined || jjb === null) return;
if (!window.chatContext) return;
window.chatContext.userJjb = Number(jjb);
window.chatContext.myGold = Number(jjb);
},
/**
* 获取快捷下注金额数组
*/
get quickBetAmounts() {
const min = this.minBet || 100;
const max = this.maxBet || 50000;
// 预设候选倍数,尽量生成美观的数字
const candidates = [1, 2, 5, 10, 20, 50, 100, 200, 500, 1000];
let steps = candidates.map(m => min * m).filter(v => v >= min && v < max);
steps.push(max);
steps = [...new Set(steps)].sort((a, b) => a - b);
if (steps.length >= 5) {
// 如果候选值足够多,均匀采样,确保首尾是最小值和最大值
return [
steps[0],
steps[Math.floor((steps.length - 1) * 0.25)],
steps[Math.floor((steps.length - 1) * 0.5)],
steps[Math.floor((steps.length - 1) * 0.75)],
steps[steps.length - 1]
];
} else {
// 如果候选值不足5个(范围太小),通过线性插值补齐
while (steps.length < 5) {
let maxGap = 0;
let insertIdx = -1;
for (let i = 0; i < steps.length - 1; i++) {
if (steps[i+1] - steps[i] > maxGap) {
maxGap = steps[i+1] - steps[i];
insertIdx = i;
}
}
if (insertIdx === -1) break;
let newVal = Math.floor((steps[insertIdx] + steps[insertIdx+1]) / 2);
if (newVal > 100) newVal = Math.floor(newVal / 10) * 10; // 简单取整
steps.splice(insertIdx + 1, 0, newVal);
}
return steps;
}
},
/**
* 从大厅或通知点击打开
*/
openFromHall() {
this.show = true;
this.loadCurrentRound();
},
/**
* 自动重置为未开局空状态
*/
setIdleState() {
clearInterval(this.countdownTimer);
clearInterval(this.autoCloseTimer);
this.phase = 'idle';
this.roundId = null;
this.countdown = 0;
this.autoCloseCountdown = 0;
this.totalBetBig = 0;
this.totalBetSmall = 0;
this.totalBetTriple = 0;
this.betCountBig = 0;
this.betCountSmall = 0;
this.betCountTriple = 0;
this.myBet = false;
this.myBetType = '';
this.myBetAmount = 0;
this.selectedType = '';
this.settledDice = [];
this.settledTotal = 0;
this.settledResult = '';
this.resultLabel = '';
this.diceEmoji = '';
this.myWon = false;
this.myPayout = 0;
this.updateFab(false);
},
/**
* 开局:填充局次数据并开始倒计时
*/
openRound(data) {
this.phase = 'betting';
this.roundId = data.round_id;
this.countdown = data.bet_seconds || 60;
this.totalSeconds = this.countdown;
this.myBet = false;
this.myBetType = '';
this.myBetAmount = 0;
this.settledDice = [];
this.selectedType = '';
this.betAmount = 100;
this.betCountBig = 0;
this.betCountSmall = 0;
this.betCountTriple = 0;
this.show = true;
this.loadCurrentRound();
this.startCountdown();
this.updateFab(true);
},
/**
* 从接口获取当前局的状态(我的下注、投注池)
*/
async loadCurrentRound() {
try {
const res = await fetch('/baccarat/current');
const data = await res.json();
// 每次打开或刷新局次信息时,都用服务端最新金币覆盖右上角余额。
this.syncUserGold(data.jjb);
if (data.round && (data.round.seconds_left || 0) > 0) {
this.phase = 'betting';
this.roundId = data.round.id;
this.countdown = data.round.seconds_left || this.countdown || 0;
this.totalBetBig = data.round.total_bet_big;
this.totalBetSmall = data.round.total_bet_small;
this.totalBetTriple = data.round.total_bet_triple;
this.betCountBig = data.round.bet_count_big;
this.betCountSmall = data.round.bet_count_small;
this.betCountTriple = data.round.bet_count_triple;
this.minBet = data.round.min_bet || 100;
this.maxBet = data.round.max_bet || 50000;
if (data.round.my_bet) {
this.myBet = true;
this.myBetType = data.round.my_bet.bet_type;
this.myBetAmount = data.round.my_bet.amount;
} else {
this.myBet = false;
this.myBetType = '';
this.myBetAmount = 0;
}
} else {
this.setIdleState();
}
} catch {
this.setIdleState();
}
},
/**
* 启动倒计时
*/
startCountdown() {
clearInterval(this.countdownTimer);
this.countdownTimer = setInterval(() => {
this.countdown--;
if (this.countdown <= 0) {
clearInterval(this.countdownTimer);
this.phase = 'waiting';
}
}, 1000);
},
/**
* 提交下注
*/
async submitBet() {
if (!this.roundId || !this.selectedType || this.betAmount < 100 || this.submitting) return;
this.submitting = true;
try {
const res = await fetch('/baccarat/bet', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
'X-CSRF-TOKEN': document.querySelector('meta[name=csrf-token]')?.content,
},
body: JSON.stringify({
round_id: this.roundId,
bet_type: this.selectedType,
amount: this.betAmount,
}),
});
const data = await res.json();
if (res.ok && data.ok) {
this.myBet = true;
this.myBetType = data.bet_type;
this.myBetAmount = data.amount;
} else if (res.status === 422 && data.errors) {
// 取出第一条 Laravel 验证失败原因
const firstError = Object.values(data.errors)[0][0];
window.chatDialog?.alert(firstError, '下注验证失败', '#ef4444');
} else {
window.chatDialog?.alert(data.message || '下注失败', '提示', '#ef4444');
}
} catch {
window.chatDialog?.alert('网络异常,请稍后重试。', '错误', '#ef4444');
}
this.submitting = false;
},
/**
* 显示开奖结果动画
*/
showResult(data) {
clearInterval(this.countdownTimer);
this.settledDice = data.dice;
this.settledTotal = data.total_points;
this.settledResult = data.result;
this.resultLabel = data.result_label;
this.phase = 'settled';
// 只有本局有押注的用户才弹出结算面板
if (this.myBet) {
this.show = true;
}
// 判断本人是否中奖(从后端拿到的 result 与我的下注 type 比较)
if (this.myBet && this.myBetType === data.result && data.result !== 'kill') {
this.myWon = true;
// 简单计算前端显示赔付(实际赔付以后端为准)
const payoutRate = data.result === 'triple' ? 24 : 1;
this.myPayout = this.myBetAmount * (payoutRate + 1);
} else {
this.myWon = false;
this.myPayout = 0;
}
this.updateFab(false);
this.loadHistory();
// 结算后 10 秒自动关闭
this.autoCloseCountdown = 10;
clearInterval(this.autoCloseTimer);
this.autoCloseTimer = setInterval(() => {
this.autoCloseCountdown--;
if (this.autoCloseCountdown <= 0) {
clearInterval(this.autoCloseTimer);
this.close();
}
}, 1000);
},
/**
* 加载历史趋势
*/
async loadHistory() {
try {
const res = await fetch('/baccarat/history');
const data = await res.json();
this.history = (data.history || []).reverse();
} catch {}
},
/**
* 更新悬浮按钮显示状态
*/
updateFab(visible) {
const fab = document.getElementById('baccarat-fab');
if (fab) Alpine.$data(fab).visible = visible;
},
/**
* 关闭面板
*/
close() {
// 手动关闭时清除自动关闭定时器
clearInterval(this.autoCloseTimer);
this.autoCloseCountdown = 0;
this.show = false;
if (this.phase === 'betting') {
this.updateFab(true); // 还在下注阶段时保留悬浮按钮
}
},
/**
* 押注类型中文标签
*/
betTypeLabel(type) {
return {
big: '大',
small: '小',
triple: '豹子'
} [type] || '';
},
};
}
{{-- 乐彩百家乐广播监听和页面恢复逻辑已迁移到 resources/js/chat-room/baccarat-events.js --}}
</script>
{{-- 乐彩百家乐主面板脚本已迁移到 resources/js/chat-room/baccarat-panel.js --}}