优化跑马页面

This commit is contained in:
2026-04-11 16:58:28 +08:00
parent abc05de86e
commit f91772b019
4 changed files with 197 additions and 35 deletions
@@ -17,9 +17,12 @@
namespace App\Http\Controllers; namespace App\Http\Controllers;
use App\Enums\CurrencySource; use App\Enums\CurrencySource;
use App\Events\MessageSent;
use App\Jobs\SaveMessageJob;
use App\Models\GameConfig; use App\Models\GameConfig;
use App\Models\HorseBet; use App\Models\HorseBet;
use App\Models\HorseRace; use App\Models\HorseRace;
use App\Services\ChatStateService;
use App\Services\UserCurrencyService; use App\Services\UserCurrencyService;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request; use Illuminate\Http\Request;
@@ -180,6 +183,24 @@ class HorseRaceController extends Controller
'status' => 'pending', 'status' => 'pending',
]); ]);
$chatState = app(ChatStateService::class);
$formattedAmount = number_format($data['amount']);
$content = "🌟 🐎 <b>{$user->username}</b> 押注了 <b>{$formattedAmount}</b> 金币({$horseName})!✨";
$msg = [
'id' => $chatState->nextMessageId(1),
'room_id' => 1,
'from_user' => '系统传音',
'to_user' => '大家',
'content' => $content,
'is_secret' => false,
'font_color' => '#d97706',
'action' => '',
'sent_at' => now()->toDateTimeString(),
];
$chatState->pushMessage(1, $msg);
event(new MessageSent(1, $msg));
SaveMessageJob::dispatch($msg);
return response()->json([ return response()->json([
'ok' => true, 'ok' => true,
'message' => "✅ 已押注「{$horseName}{$data['amount']} 金币,等待开跑!", 'message' => "✅ 已押注「{$horseName}{$data['amount']} 金币,等待开跑!",
+19
View File
@@ -127,6 +127,25 @@ export function initChat(roomId) {
window.dispatchEvent( window.dispatchEvent(
new CustomEvent("chat:baccarat.settled", { detail: e }), new CustomEvent("chat:baccarat.settled", { detail: e }),
); );
})
// ─── 赛马:开场 / 进度 / 结算 ────────────────────────────────
.listen(".horse.opened", (e) => {
console.log("赛马开场:", e);
window.dispatchEvent(
new CustomEvent("chat:horse.opened", { detail: e }),
);
})
.listen(".horse.progress", (e) => {
console.log("赛马进度:", e);
window.dispatchEvent(
new CustomEvent("chat:horse.progress", { detail: e }),
);
})
.listen(".horse.settled", (e) => {
console.log("赛马结算:", e);
window.dispatchEvent(
new CustomEvent("chat:horse.settled", { detail: e }),
);
}); });
} }
@@ -27,7 +27,8 @@
<div> <div>
<div style="color:#fff; font-weight:bold; font-size:14px;">🎲 百家乐</div> <div style="color:#fff; font-weight:bold; font-size:14px;">🎲 百家乐</div>
<div style="color:rgba(255,255,255,.75); font-size:11px; margin-top:1px;"> <div style="color:rgba(255,255,255,.75); font-size:11px; margin-top:1px;">
<span x-text="'#' + roundId"></span> <span x-show="roundId"> <span x-text="'#' + roundId"></span> </span>
<span x-show="!roundId" style="display:none;">等待开局中</span>
</div> </div>
</div> </div>
{{-- 倒计时 --}} {{-- 倒计时 --}}
@@ -42,6 +43,9 @@
<div style="color:#fbbf24; font-size:12px; font-weight:bold; margin-top:2px;" x-text="resultLabel"> <div style="color:#fbbf24; font-size:12px; font-weight:bold; margin-top:2px;" x-text="resultLabel">
</div> </div>
</div> </div>
<div x-show="phase === 'idle'" style="display:none; text-align:center;">
<div style="color:#dbeafe; font-size:11px; letter-spacing:3px;">未开始</div>
</div>
</div> </div>
{{-- 进度条 --}} {{-- 进度条 --}}
@@ -74,6 +78,23 @@
{{-- ─── 主体内容(白底) ─── --}} {{-- ─── 主体内容(白底) ─── --}}
<div style="background:#fff; padding:14px 16px;"> <div style="background:#fff; padding:14px 16px;">
{{-- 未开局状态 --}}
<div x-show="phase === 'idle'" style="display:none; text-align:center; padding:34px 12px 28px;">
<div
style="width:74px; height:74px; margin:0 auto 16px; border-radius:22px;
display:flex; align-items:center; justify-content:center;
background:linear-gradient(145deg,#eff6ff,#dbeafe);
border:1px solid #bfdbfe; box-shadow:0 10px 24px rgba(59,130,246,.16);">
<span style="font-size:34px;">🎲</span>
</div>
<div style="color:#1e3a5f; font-size:20px; font-weight:900; letter-spacing:1px;">游戏还没开启</div>
<div style="color:#7b93ad; font-size:13px; line-height:1.7; margin-top:10px;">
当前暂无进行中的百家乐局次
<br>
请等待系统开局后再来下注
</div>
</div>
{{-- 押注阶段 --}} {{-- 押注阶段 --}}
<div x-show="phase === 'betting'"> <div x-show="phase === 'betting'">
@@ -354,9 +375,12 @@
openPanel() { openPanel() {
const panel = document.getElementById('baccarat-panel'); const panel = document.getElementById('baccarat-panel');
if (!panel) return; if (!panel) {
return;
}
const p = Alpine.$data(panel); const p = Alpine.$data(panel);
p.show = true; p.show = true;
p.loadCurrentRound();
if (p.phase === 'betting' && p.countdown > 0 && !p.countdownTimer) { if (p.phase === 'betting' && p.countdown > 0 && !p.countdownTimer) {
p.startCountdown(); p.startCountdown();
} }
@@ -408,7 +432,7 @@
function baccaratPanel() { function baccaratPanel() {
return { return {
show: false, show: false,
phase: 'betting', // betting | waiting | settled phase: 'idle', // idle | betting | waiting | settled
roundId: null, roundId: null,
totalSeconds: 60, totalSeconds: 60,
@@ -451,6 +475,36 @@
autoCloseTimer: null, autoCloseTimer: null,
autoCloseCountdown: 0, autoCloseCountdown: 0,
/**
* 重置为未开局空状态
*/
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);
},
/** /**
* 开局:填充局次数据并开始倒计时 * 开局:填充局次数据并开始倒计时
*/ */
@@ -482,7 +536,10 @@
try { try {
const res = await fetch('/baccarat/current'); const res = await fetch('/baccarat/current');
const data = await res.json(); const data = await res.json();
if (data.round) { 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.totalBetBig = data.round.total_bet_big;
this.totalBetSmall = data.round.total_bet_small; this.totalBetSmall = data.round.total_bet_small;
this.totalBetTriple = data.round.total_bet_triple; this.totalBetTriple = data.round.total_bet_triple;
@@ -493,9 +550,17 @@
this.myBet = true; this.myBet = true;
this.myBetType = data.round.my_bet.bet_type; this.myBetType = data.round.my_bet.bet_type;
this.myBetAmount = data.round.my_bet.amount; this.myBetAmount = data.round.my_bet.amount;
} else {
this.myBet = false;
this.myBetType = '';
this.myBetAmount = 0;
} }
} else {
this.setIdleState();
} }
} catch {} } catch {
this.setIdleState();
}
}, },
/** /**
@@ -710,7 +775,11 @@
// 只显示悬浮按钮,不自动弹出全屏(避免打扰刚进入的用户) // 只显示悬浮按钮,不自动弹出全屏(避免打扰刚进入的用户)
panelData.updateFab(true); panelData.updateFab(true);
} else {
panelData.setIdleState();
} }
} else if (panel) {
Alpine.$data(panel).setIdleState();
} }
} catch (e) { } catch (e) {
console.warn('[百家乐] 初始化失败', e); console.warn('[百家乐] 初始化失败', e);
@@ -84,22 +84,31 @@
<div style="display:flex; flex-direction:column; gap:8px; margin-bottom:12px;"> <div style="display:flex; flex-direction:column; gap:8px; margin-bottom:12px;">
<template x-for="horse in horses" :key="horse.id"> <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;" <div style="border-radius:12px; padding:10px 14px; cursor:pointer; transition:all .15s; border:2px solid transparent;"
:style="selectedHorse === horse.id ? :style="myBet && myBetHorseId === horse.id ?
'background:#e8f0f8; border-color:#336699;' : '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);' :
'background:#f6faff; border-color:#d0e4f5;'" (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"> @click="myBet ? null : selectedHorse = horse.id">
<div style="display:flex; align-items:center; justify-content:space-between;"> <div style="display:flex; align-items:center; justify-content:space-between;">
<div style="display:flex; align-items:center; gap:10px;"> <div style="display:flex; align-items:center; gap:10px;">
{{-- 选中勾选 --}} {{-- 选中勾选 --}}
<div style="width:20px; height:20px; border-radius:50%; border:2px solid; display:flex; align-items:center; justify-content:center; font-size:10px; flex-shrink:0;" <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="selectedHorse === horse.id ? :style="myBet && myBetHorseId === horse.id ?
'border-color:#336699; background:#336699; color:#fff' : 'border-color:#16a34a; background:#16a34a; color:#fff; box-shadow:0 0 0 4px rgba(34,197,94,.16);' :
'border-color:#b0c8e0; color:transparent'"> (selectedHorse === horse.id ?
</div> '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 style="font-size:22px;" x-text="horse.emoji"></div>
<div> <div>
<div style="color:#225588; font-weight:bold; font-size:13px;" <div style="color:#225588; font-weight:bold; font-size:13px; display:flex; align-items:center; gap:6px;"
x-text="horse.name"></div> 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;"> <div style="color:#888; font-size:10px;">
注池:<span x-text="Number(horse.pool || 0).toLocaleString()"></span> 注池:<span x-text="Number(horse.pool || 0).toLocaleString()"></span>
</div> </div>
@@ -119,49 +128,66 @@
{{-- 已下注状态 --}} {{-- 已下注状态 --}}
<div x-show="myBet"> <div x-show="myBet">
<div <div
style="background:#e8fde8; border:1px solid #a3e6b0; border-radius:6px; style="background:linear-gradient(135deg,#e7fbe8,#f6fff7); border:2px solid #86efac; border-radius:14px;
padding:10px 14px; text-align:center; margin-bottom:8px;"> 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:bold; font-size:13px;"> <div style="color:#16a34a; font-weight:900; font-size:18px; letter-spacing:.02em;">
已押注「<span x-text="myBetHorseName"></span> 已押注「<span x-text="myBetHorseName"></span>
<span x-text="Number(myBetAmount).toLocaleString()"></span> 金币 <span x-text="Number(myBetAmount).toLocaleString()"></span> 金币
</div> </div>
<div style="color:#888; font-size:11px; margin-top:3px;">等待开跑…</div> <div style="color:#5f7a68; font-size:13px; margin-top:8px; font-weight:bold;">等待开跑…</div>
</div> </div>
</div> </div>
{{-- 下注区 --}} {{-- 下注区 --}}
<div x-show="!myBet"> <div x-show="!myBet">
{{-- 快捷金额 --}} {{-- 快捷金额 --}}
<div style="display:flex; gap:6px; margin-bottom:8px;"> <div style="display:grid; grid-template-columns:repeat(5,minmax(0,1fr)); gap:10px; margin-bottom:14px;">
<template x-for="preset in [100, 500, 1000, 5000, 10000]" :key="preset"> <template x-for="preset in [100, 500, 1000, 5000, 10000]" :key="preset">
<button @click="betAmount = preset" <button @click="betAmount = preset"
style="flex:1; border:1px solid #b0d0ee; border-radius:6px; padding:8px 0; style="position:relative; overflow:hidden; border:1px solid #d6e5f6; border-radius:16px; padding:12px 0;
font-size:13px; font-weight:bold; cursor:pointer; transition:all .15s;" 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 ? :style="betAmount === preset ?
'background:linear-gradient(135deg,#336699,#5a8fc0); color:#fff; border-color:#2a5580; box-shadow:0 3px 8px rgba(51,102,153,.3);' : '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:#f6faff; color:#336699;'" '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 >= 1000 ? (preset/1000)+'k' : preset"></button> x-text="preset >= 1000 ? (preset/1000)+'k' : preset"></button>
</template> </template>
</div> </div>
<input type="number" x-model.number="betAmount" min="100" placeholder="自定义金额" <input type="number" x-model.number="betAmount" min="100" placeholder="自定义金额"
style="width:100%; background:#f6faff; border:1px solid #d0e4f5; border-radius:8px; style="width:100%; background:#f6faff; border:1px solid #d0e4f5; border-radius:16px;
padding:10px 14px; color:#333; font-size:13px; box-sizing:border-box; margin-bottom:12px; outline:none; transition:all .15s;" padding:14px 16px; color:#23364d; font-size:16px; font-weight:bold; box-sizing:border-box; margin-bottom:14px; outline:none; transition:all .15s;"
onfocus="this.style.borderColor='#336699'; this.style.background='#fff'; this.style.boxShadow='0 0 0 2px rgba(51,102,153,.1)';" onfocus="this.style.borderColor='#336699'; this.style.background='#fff'; this.style.boxShadow='0 0 0 2px rgba(51,102,153,.1)';"
onblur="this.style.borderColor='#d0e4f5'; this.style.background='#f6faff'; this.style.boxShadow='none';"> onblur="this.style.borderColor='#d0e4f5'; this.style.background='#f6faff'; this.style.boxShadow='none';">
{{-- 下注按钮 --}} {{-- 下注按钮 --}}
<button @click="submitBet()" :disabled="!selectedHorse || betAmount < 100 || submitting" <button @click="submitBet()" :disabled="!selectedHorse || betAmount < 100 || submitting"
style="display:block; width:100%; border:none; border-radius:12px; padding:12px 0; style="display:flex; width:100%; align-items:center; justify-content:center; gap:10px; border:none; border-radius:22px; padding:17px 20px;
font-size:14px; font-weight:bold; cursor:pointer; transition:all .2s; box-shadow:0 4px 12px rgba(51,102,153,.2);" font-size:17px; font-weight:900; letter-spacing:.03em; cursor:pointer; transition:all .2s ease; position:relative; overflow:hidden;"
:style="(!selectedHorse || betAmount < 100 || submitting) ? :style="(!selectedHorse || betAmount < 100 || submitting) ?
'background:#e0e8f0; color:#99a8b8; cursor:not-allowed; box-shadow:none;' : 'background:linear-gradient(180deg,#edf2f8 0%,#dde5ef 100%); color:#99a8b8; cursor:not-allowed; box-shadow:inset 0 1px 0 rgba(255,255,255,.8);' :
'background:linear-gradient(135deg,#336699,#5a8fc0); color:#fff;'"> 'background:linear-gradient(180deg,#5b95cb 0%,#2f6ca3 50%,#224f7a 100%); color:#fff; box-shadow:0 16px 28px rgba(33,88,137,.32), inset 0 1px 0 rgba(255,255,255,.28), inset 0 -1px 0 rgba(0,0,0,.12); transform:translateY(-1px);'">
<span style="display:inline-flex; align-items:center; justify-content:center; width:28px; height:28px; border-radius:999px; background:rgba(255,255,255,.18); font-size:15px; box-shadow:inset 0 1px 0 rgba(255,255,255,.18);">
🐎
</span>
<span <span
x-text="submitting ? '提交中…' : (!selectedHorse ? '请先选择马匹' : '🐎 确认押注「' + myBetHorsePreviewName + '」 ' + Number(betAmount).toLocaleString() + ' 金币')"></span> x-text="submitting ? '提交中…' : (!selectedHorse ? '请先选择马匹' : '确认押注「' + myBetHorsePreviewName + '」 · ' + Number(betAmount).toLocaleString() + ' 金币')"></span>
</button> </button>
</div> </div>
</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 x-show="phase === 'running'" style="display:none;">
<div <div
@@ -183,8 +209,10 @@
'#b8d0e8')"> '#b8d0e8')">
</div> </div>
{{-- 马匹图标(跟随进度) --}} {{-- 马匹图标(跟随进度) --}}
<div style="position:absolute; top:50%; transform:translateY(-50%); font-size:16px; transition:left .9s ease-out; pointer-events:none;" <div style="position:absolute; top:50%; font-size:18px; line-height:1; transition:left .9s ease-out, transform .25s ease-out; pointer-events:none; will-change:left,transform;"
:style="'left:' + Math.max(0, (positions[horse.id] || 0) - 5) + '%'"> :style="'left:calc(' + Math.min(100, Math.max(0, positions[horse.id] || 0)) + '% - 14px); transform:translateY(-50%) ' +
((leaderId === horse.id || (positions[horse.id] || 0) > 0) ? 'scale(1.06)' : 'scale(1)') + '; ' +
((positions[horse.id] || 0) > 0 ? 'animation:horse-run .45s ease-in-out infinite alternate;' : '')">
<span x-text="horse.emoji"></span> <span x-text="horse.emoji"></span>
</div> </div>
</div> </div>
@@ -346,7 +374,7 @@
function horseRacePanel() { function horseRacePanel() {
return { return {
show: false, show: false,
phase: 'betting', // betting | running | settled phase: 'idle', // idle | betting | running | settled
raceId: null, raceId: null,
totalSeconds: 90, totalSeconds: 90,
@@ -429,9 +457,16 @@
this.myBet = true; this.myBet = true;
this.myBetHorseId = data.race.my_bet.horse_id; this.myBetHorseId = data.race.my_bet.horse_id;
this.myBetAmount = data.race.my_bet.amount; this.myBetAmount = data.race.my_bet.amount;
this.selectedHorse = data.race.my_bet.horse_id;
const h = this.horses.find(h => h.id === this.myBetHorseId); const h = this.horses.find(h => h.id === this.myBetHorseId);
this.myBetHorseName = h ? h.emoji + h.name : ''; this.myBetHorseName = h ? h.emoji + h.name : '';
} }
} else {
this.phase = 'idle';
this.raceId = null;
this.horses = [];
this.totalPool = 0;
this.countdown = 0;
} }
} catch {} } catch {}
}, },
@@ -476,10 +511,11 @@
if (data.ok) { if (data.ok) {
this.myBet = true; this.myBet = true;
this.myBetHorseId = data.horse_id; this.myBetHorseId = data.horse_id;
this.selectedHorse = data.horse_id;
this.myBetAmount = data.amount; this.myBetAmount = data.amount;
const h = this.horses.find(h => h.id === data.horse_id); const h = this.horses.find(h => h.id === data.horse_id);
this.myBetHorseName = h ? h.emoji + h.name : ''; this.myBetHorseName = h ? h.emoji + h.name : '';
window.chatDialog?.alert(data.message, '下注成功', '#f59e0b'); await this.loadCurrentRace();
} else { } else {
window.chatDialog?.alert(data.message || '下注失败', '提示', '#ef4444'); window.chatDialog?.alert(data.message || '下注失败', '提示', '#ef4444');
} }
@@ -506,6 +542,7 @@
clearInterval(this.countdownTimer); clearInterval(this.countdownTimer);
this.phase = 'settled'; this.phase = 'settled';
this.show = true; this.show = true;
this.totalPool = data.total_pool || this.totalPool;
// 找出获胜马匹信息 // 找出获胜马匹信息
const winner = this.horses.find(h => h.id === data.winner_horse_id); const winner = this.horses.find(h => h.id === data.winner_horse_id);
@@ -596,9 +633,16 @@
} }
} else { } else {
// 当前无进行中场次,重置状态 // 当前无进行中场次,重置状态
clearInterval(this.countdownTimer);
this.raceId = null; this.raceId = null;
this.horses = []; this.horses = [];
this.phase = 'betting'; this.totalPool = 0;
this.myBet = false;
this.myBetHorseId = null;
this.myBetHorseName = '';
this.myBetAmount = 0;
this.selectedHorse = null;
this.phase = 'idle';
this.countdown = 0; this.countdown = 0;
} }
} catch (e) { } catch (e) {
@@ -669,7 +713,16 @@
panelData.countdown = seconds; panelData.countdown = seconds;
} else if (race.status === 'running') { } else if (race.status === 'running') {
panelData.phase = 'running'; panelData.phase = 'running';
} else {
panelData.phase = 'settled';
} }
} else if (panel) {
const panelData = Alpine.$data(panel);
panelData.phase = 'idle';
panelData.raceId = null;
panelData.horses = [];
panelData.totalPool = 0;
panelData.countdown = 0;
} }
} catch (e) { } catch (e) {
console.warn('[赛马] 初始化失败', e); console.warn('[赛马] 初始化失败', e);