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 --}}