Files
2026-04-25 18:33:08 +08:00

338 lines
22 KiB
PHP
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.
{{--
文件功能:赛马竞猜前台弹窗组件
聊天室内赛马竞猜游戏面板:
- 监听 WebSocket horse.opened 事件触发弹窗
- 展示参赛马匹列表和实时注池赔率
- 倒计时押注后进入跑马阶段(动态进度条)
- 监听 horse.progress 更新赛道动画
- 监听 horse.settled 展示结果 + 个人赔付
- 展示近10场历史趋势
--}}
<style>
/* 赛马下注金额输入框聚焦态,替代内联 onfocus/onblur 样式。 */
.horse-race-bet-input:focus {
background: #fff !important;
border-color: #336699 !important;
box-shadow: 0 0 0 2px rgba(51, 102, 153, .1) !important;
}
</style>
{{-- ─── 赛马主面板 ─── --}}
<div id="horse-race-panel"
x-data="horseRacePanel()"
x-show="show"
x-cloak
data-horse-race-current-url="{{ route('horse-race.current') }}"
data-horse-race-bet-url="{{ route('horse-race.bet') }}"
data-horse-race-history-url="{{ route('horse-race.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"
@click.self="close()"
style="position:fixed; inset:0; background:rgba(0,0,0,.55); z-index:9941;
display:flex; align-items:center; justify-content:center;">
<div
style="width:500px; max-width:96vw; border-radius:8px; overflow:hidden;
box-shadow:0 8px 32px rgba(0,0,0,.3); font-family:'Microsoft YaHei',SimSun,sans-serif; background:#fff;">
{{-- ─── 标题栏(海军蓝风格)─── --}}
<div
style="background:linear-gradient(135deg,#336699,#5a8fc0); padding:10px 16px; display:flex; align-items:center; gap:10px;">
<div style="font-size:14px; font-weight:bold; color:#fff; flex:1;">
🐎 赛马竞猜
<span style="font-size:11px; font-weight:normal; color:rgba(255,255,255,.6); margin-left:4px;"
x-text="raceId ? '#' + raceId + ' 场' : ''"></span>
</div>
{{-- 金币余额 --}}
<div
style="font-size:12px; color:#d0e8ff; display:flex; align-items:center; gap:3px;
background:rgba(0,0,0,.2); padding:2px 8px; border-radius:10px;">
💰 <strong style="color:#ffe082; font-size:13px;" x-text="Number(window.chatContext?.userJjb || 0).toLocaleString()">--</strong> 金币
</div>
{{-- 倒计时(押注阶段) --}}
<div x-show="phase === 'betting'"
style="font-size:12px; color:#fff; background:rgba(255,255,255,.2); padding:4px 12px; border-radius:20px; border:1px solid rgba(255,255,255,.3); box-shadow:0 2px 4px rgba(0,0,0,.1) inset; display:flex; align-items:center; gap:4px;">
<span x-text="countdown" style="font-weight:bold; color:#fef08a; font-size:14px;"></span>
</div>
{{-- 跑马阶段 --}}
<div x-show="phase === 'running'" style="display:none;"
style="font-size:12px; color:rgba(255,255,255,.8); background:rgba(0,0,0,.2); padding:2px 10px; border-radius:10px;">
🏇 跑马中…
</div>
{{-- 结算 --}}
<div x-show="phase === 'settled'" style="display:none;"
style="font-size:12px; color:#ffe082; font-weight:bold;">🏆 已结算</div>
<span data-game-panel-close="horse-race-panel"
style="cursor:pointer; font-size:18px; color:rgba(255,255,255,.8); line-height:1; transition:opacity .15s;"
onmouseover="this.style.opacity=1" onmouseout="this.style.opacity=.8">&times;</span>
</div>
{{-- 押注进度条(蓝色风格) --}}
<div x-show="phase === 'betting'" style="height:3px; background:#d0e4f5; overflow:hidden;">
<div style="height:100%; background:#336699; transition:width 1s linear;"
:style="'width:' + Math.max(0, (countdown / totalSeconds * 100)) + '%'"></div>
</div>
{{-- ─── 历史趋势(蓝白色系)─── --}}
<div
style="background:#f6faff; padding:6px 16px; border-bottom:1px solid #d0e4f5;
display:flex; gap:5px; align-items:center; flex-wrap:wrap; min-height:32px;">
<span style="color:#336699; font-size:11px; margin-right:2px; font-weight:bold;">近期冒涨:</span>
<template x-for="h in history" :key="h.id">
<span
style="padding:1px 8px; border-radius:10px; font-size:10px; font-weight:bold;
background:#e8f0f8; color:#336699; border:1px solid #b8d0e8;"
:title="'#' + h.id + ' 冠军:' + h.winner_name" x-text="h.winner_name"></span>
</template>
<span x-show="history.length === 0" style="color:#aaa; font-size:11px;">暂无记录</span>
</div>
{{-- ─── 主体内容(白底)─── --}}
<div style="background:#fff; padding:14px 16px;">
{{-- ── 押注阶段 ── --}}
<div x-show="phase === 'betting'">
{{-- 注池统计 --}}
<div
style="color:#336699; font-size:11px; margin-bottom:8px; text-align:center; background:#e8f0f8; border-radius:4px; padding:4px 0;">
注池总额:<span style="color:#b45309; font-weight:bold;"
x-text="Number(totalPool).toLocaleString() + ' 金币'"></span>
</div>
{{-- 马匹列表 --}}
<div style="display:flex; flex-direction:column; gap:8px; margin-bottom:12px;">
<template x-for="horse in horses" :key="horse.id">
<div style="border-radius:12px; padding:10px 14px; cursor:pointer; transition:all .15s; border:2px solid transparent;"
:style="myBet && myBetHorseId === horse.id ?
'background:linear-gradient(135deg,#dff7e8,#f5fff8); border-color:#22c55e; box-shadow:0 0 0 3px rgba(34,197,94,.15), 0 10px 24px rgba(34,197,94,.10);' :
(selectedHorse === horse.id ?
'background:linear-gradient(135deg,#e8f0f8,#f7fbff); border-color:#336699; box-shadow:0 0 0 3px rgba(51,102,153,.12), 0 10px 24px rgba(51,102,153,.10); transform:translateY(-1px);' :
'background:#f6faff; border-color:#d0e4f5;')"
@click="myBet ? null : selectedHorse = horse.id">
<div style="display:flex; align-items:center; justify-content:space-between;">
<div style="display:flex; align-items:center; gap:10px;">
{{-- 选中勾选 --}}
<div style="width:24px; height:24px; border-radius:999px; border:2px solid; display:flex; align-items:center; justify-content:center; font-size:12px; font-weight:bold; flex-shrink:0; transition:all .15s;"
:style="myBet && myBetHorseId === horse.id ?
'border-color:#16a34a; background:#16a34a; color:#fff; box-shadow:0 0 0 4px rgba(34,197,94,.16);' :
(selectedHorse === horse.id ?
'border-color:#336699; background:#336699; color:#fff; box-shadow:0 0 0 4px rgba(51,102,153,.14);' :
'border-color:#b0c8e0; color:transparent; background:#fff;')">
<span x-text="myBet && myBetHorseId === horse.id ? '押' : '✓'"></span>
</div>
<div style="font-size:22px;" x-text="horse.emoji"></div>
<div>
<div style="color:#225588; font-weight:bold; font-size:13px; display:flex; align-items:center; gap:6px;"
x-text="horse.name"></div>
<div x-show="myBet && myBetHorseId === horse.id"
style="display:none; margin-top:2px; color:#15803d; font-size:10px; font-weight:bold;">
我的押注
</div>
<div style="color:#888; font-size:10px;">
注池:<span x-text="Number(horse.pool || 0).toLocaleString()"></span>
</div>
</div>
</div>
{{-- 实时赔率 --}}
<div style="text-align:right;">
<div style="color:#b45309; font-weight:900; font-size:15px;"
x-text="horse.odds ? '×' + horse.odds : '—'"></div>
<div style="color:#999; font-size:10px;">赔率</div>
</div>
</div>
</div>
</template>
</div>
{{-- 已下注状态 --}}
<div x-show="myBet">
<div
style="background:linear-gradient(135deg,#e7fbe8,#f6fff7); border:2px solid #86efac; border-radius:14px;
padding:14px 16px; text-align:center; margin-bottom:10px; box-shadow:0 10px 24px rgba(34,197,94,.10);">
<div style="color:#16a34a; font-weight:900; font-size:18px; letter-spacing:.02em;">
已押注「<span x-text="myBetHorseName"></span>
<span x-text="Number(myBetAmount).toLocaleString()"></span> 金币
</div>
<div style="color:#5f7a68; font-size:13px; margin-top:8px; font-weight:bold;">等待开跑…</div>
</div>
</div>
{{-- 下注区 --}}
<div x-show="!myBet">
{{-- 快捷金额 --}}
<div style="display:grid; grid-template-columns:repeat(5,minmax(0,1fr)); gap:10px; margin-bottom:14px;">
<template x-for="preset in quickBetAmounts" :key="preset">
<button @click="betAmount = preset"
style="position:relative; overflow:hidden; border:1px solid #d6e5f6; border-radius:16px; padding:12px 0;
font-size:16px; font-weight:900; letter-spacing:.01em; cursor:pointer; transition:all .18s ease; text-shadow:0 1px 0 rgba(255,255,255,.45);"
:style="betAmount === preset ?
'background:linear-gradient(180deg,#4f89c0 0%,#2d6297 52%,#214f7a 100%); color:#fff; border-color:#1c486f; box-shadow:0 12px 20px rgba(35,89,138,.30), inset 0 1px 0 rgba(255,255,255,.18); transform:translateY(-2px) scale(1.01); text-shadow:none;' :
'background:linear-gradient(180deg,#ffffff 0%,#f3f8ff 65%,#e8f1fb 100%); color:#2f6498; box-shadow:0 6px 12px rgba(76,122,172,.10), inset 0 1px 0 rgba(255,255,255,.92);'"
x-text="preset >= 10000 ? (preset/1000)+'k' : (preset >= 1000 ? (preset/1000)+'k' : preset)"></button>
</template>
</div>
<input type="number" x-model.number="betAmount" min="100" placeholder="自定义金额"
class="horse-race-bet-input"
style="width:100%; background:#f6faff; border:1px solid #d0e4f5; border-radius:16px;
padding:14px 16px; color:#23364d; font-size:16px; font-weight:bold; box-sizing:border-box; margin-bottom:14px; outline:none; transition:all .15s;">
{{-- 下注按钮 --}}
<button @click="submitBet()" :disabled="!selectedHorse || betAmount < 100 || submitting"
:style="(!selectedHorse || betAmount < 100 || submitting) ?
'display:block; width:100%; border:none; border-radius:12px; padding:13px 0; font-size:14px; font-weight:bold; cursor:not-allowed; transition:all .2s; background:#e0e8f0; color:#99a8b8; box-shadow:none; font-family:inherit;' :
'display:block; width:100%; border:none; border-radius:12px; padding:13px 0; font-size:14px; font-weight:bold; cursor:pointer; transition:all .2s; background:linear-gradient(135deg,#336699,#5a8fc0); color:#fff; box-shadow:0 4px 14px rgba(51,102,153,.3); font-family:inherit;'">
<span
x-text="submitting ? '提交中…' : (!selectedHorse ? '请先选择马匹' : '🐎 押注「' + myBetHorsePreviewName + '」 ' + Number(betAmount).toLocaleString() + ' 金币')"></span>
</button>
</div>
</div>
{{-- ── 未开始阶段 ── --}}
<div x-show="phase === 'idle'" style="display:none;">
<div style="padding:16px 4px 10px;">
<div style="background:linear-gradient(135deg,#f7fbff,#eef6ff); border:1px solid #c9def2; border-radius:16px; padding:22px 18px; text-align:center; box-shadow:inset 0 1px 0 rgba(255,255,255,.9);">
<div style="font-size:34px; margin-bottom:8px;">🐎</div>
<div style="color:#24507a; font-size:18px; font-weight:900; margin-bottom:6px;">游戏还没开始</div>
<div style="color:#6b7f95; font-size:13px; line-height:1.7;">
当前暂无进行中的赛马场次。<br>
请等待系统开场后再来下注。
</div>
</div>
</div>
</div>
{{-- ── 跑马阶段 ── --}}
<div x-show="phase === 'running'" style="display:none;">
<div
style="margin-bottom:8px; color:#336699; font-size:11px; font-weight:bold; text-align:center; background:#e8f0f8; border-radius:4px; padding:4px;">
🏁 赛道实况
</div>
<div style="display:flex; flex-direction:column; gap:8px;">
<template x-for="horse in horses" :key="horse.id">
<div style="display:flex; align-items:center; gap:8px;">
<div style="width:30px; text-align:center; font-size:18px;" x-text="horse.emoji"></div>
<div style="flex:1; position:relative;">
{{-- 赛道背景 --}}
<div
style="height:24px; background:#e8f0f8; border-radius:10px; overflow:hidden; position:relative;">
{{-- 进度条 --}}
<div style="height:100%; border-radius:20px; transition:width .9s ease-out;"
:style="'width:' + (positions[horse.id] || 0) + '%; background:' +
(leaderId === horse.id ? '#336699' :
'#b8d0e8')">
</div>
{{-- 马匹图标(跟随进度) --}}
<div style="position:absolute; top:0; bottom:0; display:flex; align-items:center; font-size:18px; line-height:1; transition:left .9s ease-out, transform .25s ease-out; pointer-events:none; will-change:left,transform; z-index:1;"
:style="{
left: (positions[horse.id] || 0) + '%',
transform: 'translateX(-100%) ' + (leaderId === horse.id ? 'scale(1.2)' : 'scale(1)'),
animation: (positions[horse.id] || 0) > 0 ? 'horse-run .45s ease-in-out infinite alternate' : 'none'
}">
<span x-text="horse.emoji"></span>
</div>
</div>
</div>
{{-- 进度数字 --}}
<div style="width:38px; text-align:right; color:#336699; font-size:10px; font-weight:bold;"
x-text="(positions[horse.id] || 0) + '%'"></div>
</div>
</template>
</div>
<div style="margin-top:10px; text-align:center; color:#aaa; font-size:10px; text-align:center;">
🏁 终点线
</div>
</div>
{{-- ── 结算阶段(蓝白风格)── --}}
<div x-show="phase === 'settled'" style="display:none;">
{{-- 获胜马匹 --}}
<div
style="text-align:center; padding:12px 0 10px; border-bottom:1px solid #d0e4f5; margin-bottom:10px;">
<div style="font-size:36px; margin-bottom:4px;" x-text="winnerEmoji"></div>
<div style="color:#336699; font-size:17px; font-weight:bold;"
x-text="'🏆 ' + winnerName + ' 夺冠!'">
</div>
<div style="color:#888; font-size:11px; margin-top:3px;"
x-text="'注池总额:' + Number(totalPool).toLocaleString() + ' 金币'"></div>
</div>
{{-- 个人结果 --}}
<div x-show="myBet">
{{-- 中奖 --}}
<div x-show="myWon"
style="border-radius:6px; padding:12px 14px; text-align:center; margin-bottom:4px;
background:#e8fde8; border:1px solid #a3e6b0;">
<div style="font-size:24px; margin-bottom:4px;">🎉</div>
<div style="color:#16a34a; font-size:16px; font-weight:bold;">恭喜中奖!</div>
<div style="color:#15803d; font-size:18px; font-weight:bold; margin:4px 0;"
x-text="'+' + Number(myPayout).toLocaleString() + ' 💰'"></div>
<div style="color:#888; font-size:10px;"
x-text="'押「' + myBetHorseName + '」' + Number(myBetAmount).toLocaleString() + ' 金币 → 赢得 ' + Number(myPayout).toLocaleString() + ' 金币'">
</div>
</div>
{{-- 未中奖 --}}
<div x-show="!myWon"
style="border-radius:6px; padding:12px 14px; text-align:center;
background:#fff0f0; border:1px solid #fca5a5;">
<div style="font-size:20px; margin-bottom:4px;">😔</div>
<div style="color:#dc2626; font-size:13px; font-weight:bold; margin-bottom:4px;">本场未中奖
</div>
<div style="color:#888; font-size:11px;"
x-text="'押了「' + myBetHorseName + '」' + Number(myBetAmount).toLocaleString() + ' 金币,冠军是「' + winnerName + '」'">
</div>
</div>
</div>
<div x-show="!myBet" style="text-align:center; color:#aaa; font-size:12px; padding:8px 0;">
本场未参与下注
</div>
</div>
</div>
{{-- ─── 底部关闭 ─── --}}
<div
style="background:#fff; border-top:1px solid #d0e4f5; padding:14px 16px; display:flex; flex-direction:column; align-items:center; gap:8px;">
<button @click="close()"
style="padding:10px 48px; min-width:140px; background:#f0f6ff; border:1px solid #b0d0ee; border-radius:12px;
font-size:14px; font-weight:bold; color:#336699; cursor:pointer; transition:all .15s;"
onmouseover="this.style.background='#ddeeff'" onmouseout="this.style.background='#f0f6ff'">
<span x-show="phase !== 'settled'">关闭</span>
<span x-show="phase === 'settled'" x-text="'关闭 (' + settledCountdown + 's)'"></span>
</button>
<div x-show="phase === 'settled'" style="font-size:11px; color:#94a3b8;">窗口将在倒计时结束后自动关闭</div>
</div>
</div>
</div>
</div>
<style>
@keyframes horse-run {
0% {
transform: translateX(-2px);
}
100% {
transform: translateX(2px);
}
}
@keyframes pulse-horse {
0%,
100% {
box-shadow: 0 4px 20px rgba(245, 158, 11, .5);
}
50% {
box-shadow: 0 4px 32px rgba(245, 158, 11, .9);
}
}
</style>
{{-- 赛马竞猜主面板脚本已迁移到 resources/js/chat-room/horse-race-panel.js --}}