diff --git a/resources/js/chat-room.js b/resources/js/chat-room.js index 2d673bb..2e1bc3d 100644 --- a/resources/js/chat-room.js +++ b/resources/js/chat-room.js @@ -37,7 +37,13 @@ export { openAdminBaccaratLossCoverModal, submitBaccaratLossCoverEvent, } from "./chat-room/baccarat-loss-cover-admin.js"; -export { bindBaccaratLossCoverControls } from "./chat-room/baccarat-loss-cover.js"; +export { + bindBaccaratLossCoverControls, + claimBaccaratLossCover, + closeBaccaratLossCoverModal, + openBaccaratLossCoverModal, + switchBaccaratLossCoverTab, +} from "./chat-room/baccarat-loss-cover.js"; export { bindGameHallControls } from "./chat-room/game-hall.js"; export { bindGameBootstrapControls, deferChatGameBootstrap } from "./chat-room/game-bootstrap.js"; export { bindGamePanelControls } from "./chat-room/game-panels.js"; @@ -121,7 +127,13 @@ import { openAdminBaccaratLossCoverModal, submitBaccaratLossCoverEvent, } from "./chat-room/baccarat-loss-cover-admin.js"; -import { bindBaccaratLossCoverControls } from "./chat-room/baccarat-loss-cover.js"; +import { + bindBaccaratLossCoverControls, + claimBaccaratLossCover, + closeBaccaratLossCoverModal, + openBaccaratLossCoverModal, + switchBaccaratLossCoverTab, +} from "./chat-room/baccarat-loss-cover.js"; import { bindGameHallControls } from "./chat-room/game-hall.js"; import { bindGameBootstrapControls, deferChatGameBootstrap } from "./chat-room/game-bootstrap.js"; import { bindGamePanelControls } from "./chat-room/game-panels.js"; @@ -217,6 +229,10 @@ if (typeof window !== "undefined") { closeAdminBaccaratLossCoverModal, closeCurrentBaccaratLossCoverEvent, bindBaccaratLossCoverControls, + claimBaccaratLossCover, + closeBaccaratLossCoverModal, + openBaccaratLossCoverModal, + switchBaccaratLossCoverTab, bindGameHallControls, bindGameBootstrapControls, deferChatGameBootstrap, @@ -298,8 +314,12 @@ if (typeof window !== "undefined") { window.openHolidayRunFromSystemMessage = openHolidayRunFromSystemMessage; window.closeAdminBaccaratLossCoverModal = closeAdminBaccaratLossCoverModal; window.closeCurrentBaccaratLossCoverEvent = closeCurrentBaccaratLossCoverEvent; + window.claimBaccaratLossCover = claimBaccaratLossCover; + window.closeBaccaratLossCoverModal = closeBaccaratLossCoverModal; + window.openBaccaratLossCoverModal = openBaccaratLossCoverModal; window.openAdminBaccaratLossCoverModal = openAdminBaccaratLossCoverModal; window.submitBaccaratLossCoverEvent = submitBaccaratLossCoverEvent; + window.switchBaccaratLossCoverTab = switchBaccaratLossCoverTab; window.bankAction = bankAction; window.bankLoadInfo = bankLoadInfo; window.bankShowMsg = bankShowMsg; diff --git a/resources/js/chat-room/baccarat-loss-cover.js b/resources/js/chat-room/baccarat-loss-cover.js index 7934129..3a5dfeb 100644 --- a/resources/js/chat-room/baccarat-loss-cover.js +++ b/resources/js/chat-room/baccarat-loss-cover.js @@ -1,18 +1,360 @@ -// 百乐加强买单活动前台弹窗事件代理,替代 Blade 内联 onclick。 +// 百乐加强买单活动前台弹窗逻辑,负责活动摘要、历史记录和补偿领取。 + +import { escapeHtml } from "./html.js"; let baccaratLossCoverEventsBound = false; /** - * 调用买单活动前台存量全局函数。 + * 获取前台买单活动弹窗。 * - * @param {string} functionName 全局函数名 - * @param {...unknown} args 参数 + * @returns {HTMLElement|null} + */ +function getLossCoverModal() { + return document.getElementById("baccarat-loss-cover-modal"); +} + +/** + * 读取 Blade 注入到弹窗上的接口地址。 + * + * @returns {{summaryUrl:string,historyUrl:string,claimUrlTemplate:string}|null} + */ +function resolveLossCoverUrls() { + const modal = getLossCoverModal(); + if (!modal) { + return null; + } + + return { + summaryUrl: modal.dataset.blcSummaryUrl || "", + historyUrl: modal.dataset.blcHistoryUrl || "", + claimUrlTemplate: modal.dataset.blcClaimUrlTemplate || "", + }; +} + +/** + * 生成补偿领取接口地址。 + * + * @param {number|string} eventId 活动 ID + * @returns {string} + */ +function resolveClaimUrl(eventId) { + const urls = resolveLossCoverUrls(); + + return urls?.claimUrlTemplate.replace("__EVENT__", encodeURIComponent(String(eventId))) || ""; +} + +/** + * 获取 CSRF Token。 + * + * @returns {string} + */ +function csrf() { + return document.querySelector('meta[name="csrf-token"]')?.content || ""; +} + +/** + * 安全格式化活动时间。 + * + * @param {string|null|undefined} value + * @returns {string} + */ +function formatTime(value) { + if (!value) { + return "—"; + } + + return new Date(value).toLocaleString("zh-CN", { + month: "2-digit", + day: "2-digit", + hour: "2-digit", + minute: "2-digit", + }); +} + +/** + * 同步当前用户金币到全局上下文和相关展示节点。 + * + * @param {number} amount * @returns {void} */ -function callLossCoverGlobal(functionName, ...args) { - // 前台弹窗的数据加载、领取接口和金币同步仍由 Blade 旧脚本维护。 - if (typeof window[functionName] === "function") { - window[functionName](...args); +function syncUserGold(amount) { + if (!Number.isFinite(amount) || !window.chatContext) { + return; + } + + window.chatContext.userJjb = Number(window.chatContext.userJjb || 0) + amount; + window.chatContext.myGold = Number(window.chatContext.myGold || 0) + amount; + + const hallGold = document.getElementById("game-hall-jjb"); + if (hallGold) { + hallGold.textContent = Number(window.chatContext.userJjb || 0).toLocaleString(); + } + + const modalGold = document.getElementById("blc-modal-jjb"); + if (modalGold) { + modalGold.textContent = Number(window.chatContext.userJjb || 0).toLocaleString(); + } +} + +/** + * 渲染当前买单活动摘要。 + * + * @param {object|null} event + * @returns {void} + */ +function renderCurrentEvent(event) { + const container = document.getElementById("blc-current-event"); + if (!container) { + return; + } + + if (!event) { + container.innerHTML = ` +
📭
+
当前暂无进行中的买单活动
+
可以在输入框上方的管理员按钮中创建新活动,也可以在这里查看历史记录。
+ `; + return; + } + + const myRecord = event.my_record; + const claimButton = event.status === "claimable" && myRecord?.claim_status === "pending" + ? `` + : ""; + + // 服务端活动字段进入 HTML 前统一转义,避免标题、说明或用户名污染 DOM。 + container.innerHTML = ` +
+
+
${escapeHtml(String(event.title || ""))}
+
${escapeHtml(String(event.description || "活动期间参与百家乐,输掉的金币可在活动结束后领取补偿。"))}
+
+ ${escapeHtml(String(event.status_label || ""))} +
+
+
+
开启人
+
${escapeHtml(String(event.creator_username || ""))}
+
+
+
活动时间
+
${escapeHtml(formatTime(event.starts_at))} - ${escapeHtml(formatTime(event.ends_at))}
+
+
+
最终已发补偿
+
${Number(event.total_claimed_amount || 0).toLocaleString()} 金币
+
+
+
我的状态
+
${escapeHtml(String(myRecord?.claim_status_label || "未参与"))}
+
+
+ ${myRecord ? ` +
+
我的活动记录
+
+
累计下注:${Number(myRecord.total_bet_amount || 0).toLocaleString()}
+
累计输掉:${Number(myRecord.total_loss_amount || 0).toLocaleString()}
+
可领补偿:${Number(myRecord.compensation_amount || 0).toLocaleString()}
+
已领补偿:${Number(myRecord.claimed_amount || 0).toLocaleString()}
+
+ ${claimButton ? `
${claimButton}
` : ""} +
+ ` : ""} + `; +} + +/** + * 渲染买单活动历史记录。 + * + * @param {object[]} events + * @returns {void} + */ +function renderHistory(events) { + const container = document.getElementById("blc-history-list"); + if (!container) { + return; + } + + if (!events || events.length === 0) { + container.innerHTML = '
暂无活动记录
'; + return; + } + + container.innerHTML = events.map((event) => { + const myRecord = event.my_record; + const claimButton = event.status === "claimable" && myRecord?.claim_status === "pending" + ? `` + : ""; + + return ` +
+
+
+
${escapeHtml(String(event.title || ""))}
+
开启人:${escapeHtml(String(event.creator_username || ""))} | ${escapeHtml(formatTime(event.starts_at))} - ${escapeHtml(formatTime(event.ends_at))}
+
+ ${escapeHtml(String(event.status_label || ""))} +
+
+
最终补偿发放:${Number(event.total_claimed_amount || 0).toLocaleString()}
+
本次总输金币:${Number(event.total_loss_amount || 0).toLocaleString()}
+
我的状态:${escapeHtml(String(myRecord?.claim_status_label || "未参与"))}
+
我的可领:${Number(myRecord?.compensation_amount || 0).toLocaleString()}
+
+ ${myRecord ? `
累计下注 ${Number(myRecord.total_bet_amount || 0).toLocaleString()} | 累计输掉 ${Number(myRecord.total_loss_amount || 0).toLocaleString()} | 已领 ${Number(myRecord.claimed_amount || 0).toLocaleString()}
` : ""} + ${claimButton ? `
${claimButton}
` : ""} +
+ `; + }).join(""); +} + +/** + * 加载当前活动摘要。 + * + * @returns {Promise} + */ +async function loadSummary() { + const current = document.getElementById("blc-current-event"); + const urls = resolveLossCoverUrls(); + if (!current || !urls?.summaryUrl) { + return; + } + + current.innerHTML = "加载中…"; + + try { + const response = await fetch(`${urls.summaryUrl}?scene=overview`, { + headers: { + Accept: "application/json", + }, + }); + const data = await response.json(); + renderCurrentEvent(data.event || null); + } catch (error) { + current.innerHTML = '
活动摘要加载失败,请稍后重试。
'; + } +} + +/** + * 加载活动历史记录。 + * + * @returns {Promise} + */ +async function loadHistory() { + const list = document.getElementById("blc-history-list"); + const urls = resolveLossCoverUrls(); + if (!list || !urls?.historyUrl) { + return; + } + + list.innerHTML = "加载中…"; + + try { + const response = await fetch(urls.historyUrl, { + headers: { + Accept: "application/json", + }, + }); + const data = await response.json(); + renderHistory(data.events || []); + } catch (error) { + list.innerHTML = '
活动历史加载失败,请稍后重试。
'; + } +} + +/** + * 切换买单活动弹窗 Tab。 + * + * @param {string} tab + * @returns {void} + */ +export function switchBaccaratLossCoverTab(tab) { + const overview = document.getElementById("blc-overview-pane"); + const history = document.getElementById("blc-history-pane"); + const overviewButton = document.getElementById("blc-tab-overview"); + const historyButton = document.getElementById("blc-tab-history"); + + if (!overview || !history || !overviewButton || !historyButton) { + return; + } + + const showingHistory = tab === "history"; + overview.style.display = showingHistory ? "none" : "block"; + history.style.display = showingHistory ? "block" : "none"; + overviewButton.style.background = showingHistory ? "#dcfce7" : "#15803d"; + overviewButton.style.color = showingHistory ? "#166534" : "#fff"; + historyButton.style.background = showingHistory ? "#15803d" : "#dcfce7"; + historyButton.style.color = showingHistory ? "#fff" : "#166534"; +} + +/** + * 打开买单活动前台弹窗。 + * + * @param {string} [tab] + * @returns {Promise} + */ +export async function openBaccaratLossCoverModal(tab = "overview") { + const modal = getLossCoverModal(); + if (!modal) { + return; + } + + modal.style.display = "flex"; + + const modalGold = document.getElementById("blc-modal-jjb"); + if (modalGold) { + modalGold.textContent = Number(window.chatContext?.userJjb || 0).toLocaleString(); + } + + switchBaccaratLossCoverTab(tab); + await Promise.all([loadSummary(), loadHistory()]); +} + +/** + * 关闭买单活动前台弹窗。 + * + * @returns {void} + */ +export function closeBaccaratLossCoverModal() { + const modal = getLossCoverModal(); + if (modal) { + modal.style.display = "none"; + } +} + +/** + * 领取买单活动补偿。 + * + * @param {number|string} eventId + * @returns {Promise} + */ +export async function claimBaccaratLossCover(eventId) { + const claimUrl = resolveClaimUrl(eventId); + if (!claimUrl) { + return; + } + + try { + const response = await fetch(claimUrl, { + method: "POST", + headers: { + "X-CSRF-TOKEN": csrf(), + Accept: "application/json", + }, + }); + const data = await response.json(); + + if (data.ok) { + syncUserGold(Number(data.amount || 0)); + await window.chatDialog?.alert?.(data.message || "补偿领取成功", "系统通知", "#16a34a"); + await Promise.all([loadSummary(), loadHistory()]); + return; + } + + await window.chatDialog?.alert?.(data.message || "领取失败", "提示", "#f59e0b"); + } catch (error) { + await window.chatDialog?.alert?.("领取失败,请稍后重试。", "提示", "#dc2626"); } } @@ -27,6 +369,12 @@ export function bindBaccaratLossCoverControls() { } baccaratLossCoverEventsBound = true; + + window.openBaccaratLossCoverModal = openBaccaratLossCoverModal; + window.closeBaccaratLossCoverModal = closeBaccaratLossCoverModal; + window.switchBaccaratLossCoverTab = switchBaccaratLossCoverTab; + window.claimBaccaratLossCover = claimBaccaratLossCover; + document.addEventListener("click", (event) => { if (!(event.target instanceof Element)) { return; @@ -34,22 +382,27 @@ export function bindBaccaratLossCoverControls() { if (event.target.closest("[data-blc-close]")) { event.preventDefault(); - callLossCoverGlobal("closeBaccaratLossCoverModal"); + closeBaccaratLossCoverModal(); return; } const tabButton = event.target.closest("[data-blc-tab]"); if (tabButton) { event.preventDefault(); - callLossCoverGlobal("switchBaccaratLossCoverTab", tabButton.getAttribute("data-blc-tab") || "overview"); + switchBaccaratLossCoverTab(tabButton.getAttribute("data-blc-tab") || "overview"); return; } const claimButton = event.target.closest("[data-blc-claim]"); if (claimButton) { event.preventDefault(); - // 动态活动卡片只传活动 ID,领取流程继续复用旧全局函数。 - callLossCoverGlobal("claimBaccaratLossCover", claimButton.getAttribute("data-blc-claim") || ""); + void claimBaccaratLossCover(claimButton.getAttribute("data-blc-claim") || ""); + return; + } + + const modal = event.target.closest("#baccarat-loss-cover-modal"); + if (modal && event.target === modal) { + closeBaccaratLossCoverModal(); } }); } diff --git a/resources/views/chat/partials/games/baccarat-loss-cover-panel.blade.php b/resources/views/chat/partials/games/baccarat-loss-cover-panel.blade.php index 8e89b45..d4681a8 100644 --- a/resources/views/chat/partials/games/baccarat-loss-cover-panel.blade.php +++ b/resources/views/chat/partials/games/baccarat-loss-cover-panel.blade.php @@ -6,6 +6,9 @@ --}} - +{{-- 前台买单活动脚本已迁移到 resources/js/chat-room/baccarat-loss-cover.js --}}