From 63f6dc71061016168b3bf523a5d956dc658c96eb Mon Sep 17 00:00:00 2001 From: lkddi Date: Sat, 25 Apr 2026 18:17:11 +0800 Subject: [PATCH] =?UTF-8?q?=E8=BF=81=E7=A7=BB=E8=B5=9B=E9=A9=AC=E4=BA=8B?= =?UTF-8?q?=E4=BB=B6=E8=84=9A=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- resources/js/chat-room.js | 5 + resources/js/chat-room/horse-race-events.js | 140 ++++++++++++++++++ .../partials/games/horse-race-panel.blade.php | 77 +--------- 3 files changed, 146 insertions(+), 76 deletions(-) create mode 100644 resources/js/chat-room/horse-race-events.js diff --git a/resources/js/chat-room.js b/resources/js/chat-room.js index ab2f424..4e19405 100644 --- a/resources/js/chat-room.js +++ b/resources/js/chat-room.js @@ -31,6 +31,7 @@ * - game-panels.js:处理通用游戏面板关闭事件。 * - gomoku-controls.js:处理五子棋外部打开和接受邀请入口。 * - horse-race-fab.js:处理赛马竞猜悬浮按钮拖动与打开面板。 + * - horse-race-events.js:处理赛马广播事件和页面恢复当前场次。 * - holiday-modal.js:处理节日福利弹窗、广播监听、领取状态和系统消息入口。 * - initial-state.js:恢复首屏历史消息、欢迎消息、入场特效和挂起婚姻事件。 * - bank-modal.js:处理银行弹窗、转账、排行和标签切换。 @@ -110,6 +111,7 @@ export { bindGameBootstrapControls, deferChatGameBootstrap } from "./chat-room/g export { bindGamePanelControls } from "./chat-room/game-panels.js"; export { acceptGomokuInvite, bindGomokuControls, openGomokuPanel } from "./chat-room/gomoku-controls.js"; export { bindHorseRaceFabControls, horseRaceFab } from "./chat-room/horse-race-fab.js"; +export { bindHorseRaceEvents } from "./chat-room/horse-race-events.js"; export { bindHolidayModalControls, buildHolidayClaimActionButton, @@ -256,6 +258,7 @@ import { bindGameBootstrapControls, deferChatGameBootstrap } from "./chat-room/g import { bindGamePanelControls } from "./chat-room/game-panels.js"; import { acceptGomokuInvite, bindGomokuControls, openGomokuPanel } from "./chat-room/gomoku-controls.js"; import { bindHorseRaceFabControls, horseRaceFab } from "./chat-room/horse-race-fab.js"; +import { bindHorseRaceEvents } from "./chat-room/horse-race-events.js"; import { bindHolidayModalControls, buildHolidayClaimActionButton, @@ -412,6 +415,7 @@ if (typeof window !== "undefined") { openGomokuPanel, bindHorseRaceFabControls, horseRaceFab, + bindHorseRaceEvents, bindHolidayModalControls, buildHolidayClaimActionButton, buildHolidaySystemMessage, @@ -654,6 +658,7 @@ if (typeof window !== "undefined") { bindGamePanelControls(); bindGomokuControls(); bindHorseRaceFabControls(); + bindHorseRaceEvents(); bindHolidayModalControls(); bindChatInitialStateControls(); bindBankControls(); diff --git a/resources/js/chat-room/horse-race-events.js b/resources/js/chat-room/horse-race-events.js new file mode 100644 index 0000000..c8e4344 --- /dev/null +++ b/resources/js/chat-room/horse-race-events.js @@ -0,0 +1,140 @@ +// 赛马竞猜事件监听模块,负责广播事件转发和页面恢复当前场次。 + +/** + * 读取赛马面板 Alpine 状态。 + * + * @returns {Record|null} + */ +function getHorseRacePanelState() { + const panel = document.getElementById("horse-race-panel"); + + if (!panel || typeof window.Alpine?.$data !== "function") { + return null; + } + + return window.Alpine.$data(panel); +} + +/** + * 读取赛马悬浮按钮 Alpine 状态。 + * + * @returns {Record|null} + */ +function getHorseRaceFabState() { + const fab = document.getElementById("horse-race-fab"); + + if (!fab || typeof window.Alpine?.$data !== "function") { + return null; + } + + return window.Alpine.$data(fab); +} + +/** + * 从当前进行中的场次恢复赛马面板状态。 + * + * @param {Record} panelState 赛马面板 Alpine 状态 + * @param {Record} race 当前场次 + * @returns {void} + */ +function restoreCurrentHorseRace(panelState, race) { + const seconds = Number(race.seconds_left || 0); + + panelState.raceId = race.id; + panelState.horses = race.horses || []; + panelState.totalPool = race.total_pool || 0; + + if (race.my_bet) { + panelState.myBet = true; + panelState.myBetHorseId = race.my_bet.horse_id; + panelState.myBetAmount = race.my_bet.amount; + + const selectedHorse = panelState.horses.find((horse) => horse.id === race.my_bet.horse_id); + panelState.myBetHorseName = selectedHorse ? `${selectedHorse.emoji}${selectedHorse.name}` : ""; + } + + if (race.status === "betting" && seconds > 0) { + panelState.phase = "betting"; + panelState.countdown = seconds; + return; + } + + panelState.phase = race.status === "running" ? "running" : "settled"; +} + +/** + * 没有当前场次时重置赛马面板状态。 + * + * @param {Record} panelState 赛马面板 Alpine 状态 + * @returns {void} + */ +function resetHorseRacePanel(panelState) { + panelState.phase = "idle"; + panelState.raceId = null; + panelState.horses = []; + panelState.totalPool = 0; + panelState.countdown = 0; +} + +/** + * 页面加载后恢复历史记录、金币和当前场次。 + * + * @returns {Promise} + */ +async function restoreHorseRaceOnReady() { + try { + const panelState = getHorseRacePanelState(); + + if (!panelState) { + return; + } + + const historyData = await panelState.requestJson("/horse-race/history"); + panelState.history = (historyData.history || []).reverse(); + + const currentData = await panelState.requestJson("/horse-race/current"); + panelState.syncUserGold(currentData.jjb); + + // 游戏可访问时常驻显示 FAB,方便用户随时打开赛马面板。 + const fabState = getHorseRaceFabState(); + if (fabState) { + fabState.visible = true; + } + + if (currentData.race) { + restoreCurrentHorseRace(panelState, currentData.race); + return; + } + + resetHorseRacePanel(panelState); + } catch (error) { + console.warn("[赛马] 初始化失败", error); + } +} + +/** + * 绑定赛马广播事件和页面恢复逻辑。 + * + * @returns {void} + */ +export function bindHorseRaceEvents() { + if (typeof window === "undefined" || typeof document === "undefined" || window.__horseRaceEventsBound) { + return; + } + + window.__horseRaceEventsBound = true; + + window.addEventListener("chat:horse.opened", (event) => { + getHorseRacePanelState()?.openRace(event.detail); + }); + + window.addEventListener("chat:horse.progress", (event) => { + getHorseRacePanelState()?.updateProgress(event.detail); + }); + + window.addEventListener("chat:horse.settled", (event) => { + getHorseRacePanelState()?.showResult(event.detail); + }); + + document.addEventListener("DOMContentLoaded", () => window.deferChatGameBootstrap?.(restoreHorseRaceOnReady)); +} diff --git a/resources/views/chat/partials/games/horse-race-panel.blade.php b/resources/views/chat/partials/games/horse-race-panel.blade.php index 0853c28..c35f1e0 100644 --- a/resources/views/chat/partials/games/horse-race-panel.blade.php +++ b/resources/views/chat/partials/games/horse-race-panel.blade.php @@ -727,80 +727,5 @@ }; } - // ─── WebSocket 监听 ────────────────────────────────────────────── - - /** 收到开赛事件:弹出押注面板 */ - window.addEventListener('chat:horse.opened', (e) => { - const panel = document.getElementById('horse-race-panel'); - if (panel) Alpine.$data(panel).openRace(e.detail); - }); - - /** 收到跑马进度事件:更新赛道 */ - window.addEventListener('chat:horse.progress', (e) => { - const panel = document.getElementById('horse-race-panel'); - if (panel) Alpine.$data(panel).updateProgress(e.detail); - }); - - /** 收到结算事件:展示结果 */ - window.addEventListener('chat:horse.settled', (e) => { - const panel = document.getElementById('horse-race-panel'); - if (panel) Alpine.$data(panel).showResult(e.detail); - }); - - /** 页面加载时恢复进行中的场次 */ - document.addEventListener('DOMContentLoaded', () => window.deferChatGameBootstrap(async () => { - try { - const panel = document.getElementById('horse-race-panel'); - const histData = panel ? await Alpine.$data(panel).requestJson('/horse-race/history') : { history: [] }; - const fab = document.getElementById('horse-race-fab'); - - if (panel) { - Alpine.$data(panel).history = (histData.history || []).reverse(); - } - - const curData = panel ? await Alpine.$data(panel).requestJson('/horse-race/current') : { race: null }; - if (panel) { - Alpine.$data(panel).syncUserGold(curData.jjb); - } - - // 游戏可访问则常驻显示 FAB(与占卜一致) - if (fab) Alpine.$data(fab).visible = true; - - if (curData.race && panel) { - const race = curData.race; - const seconds = race.seconds_left || 0; - const panelData = Alpine.$data(panel); - - panelData.raceId = race.id; - panelData.horses = race.horses || []; - panelData.totalPool = race.total_pool || 0; - - if (race.my_bet) { - panelData.myBet = true; - panelData.myBetHorseId = race.my_bet.horse_id; - panelData.myBetAmount = race.my_bet.amount; - const h = panelData.horses.find(h => h.id === race.my_bet.horse_id); - panelData.myBetHorseName = h ? h.emoji + h.name : ''; - } - - if (race.status === 'betting' && seconds > 0) { - panelData.phase = 'betting'; - panelData.countdown = seconds; - } else if (race.status === '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) { - console.warn('[赛马] 初始化失败', e); - } - })); + {{-- 赛马广播监听和页面恢复逻辑已迁移到 resources/js/chat-room/horse-race-events.js --}}