// 百乐加强买单活动前台弹窗逻辑,负责活动摘要、历史记录和补偿领取。 import { escapeHtml } from "./html.js"; let baccaratLossCoverEventsBound = false; /** * 获取前台买单活动弹窗。 * * @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 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"); } } /** * 绑定买单活动前台弹窗关闭、Tab 切换与领取按钮事件。 * * @returns {void} */ export function bindBaccaratLossCoverControls() { if (baccaratLossCoverEventsBound || typeof document === "undefined") { return; } baccaratLossCoverEventsBound = true; window.openBaccaratLossCoverModal = openBaccaratLossCoverModal; window.closeBaccaratLossCoverModal = closeBaccaratLossCoverModal; window.switchBaccaratLossCoverTab = switchBaccaratLossCoverTab; window.claimBaccaratLossCover = claimBaccaratLossCover; document.addEventListener("click", (event) => { if (!(event.target instanceof Element)) { return; } if (event.target.closest("[data-blc-close]")) { event.preventDefault(); closeBaccaratLossCoverModal(); return; } const tabButton = event.target.closest("[data-blc-tab]"); if (tabButton) { event.preventDefault(); switchBaccaratLossCoverTab(tabButton.getAttribute("data-blc-tab") || "overview"); return; } const claimButton = event.target.closest("[data-blc-claim]"); if (claimButton) { event.preventDefault(); void claimBaccaratLossCover(claimButton.getAttribute("data-blc-claim") || ""); return; } const modal = event.target.closest("#baccarat-loss-cover-modal"); if (modal && event.target === modal) { closeBaccaratLossCoverModal(); } }); }