diff --git a/resources/js/chat-room.js b/resources/js/chat-room.js index 4fd1661..f74e12c 100644 --- a/resources/js/chat-room.js +++ b/resources/js/chat-room.js @@ -11,6 +11,14 @@ export { bindMobileDrawerControls } from "./chat-room/mobile-drawer.js"; export { bindToolbarControls } from "./chat-room/toolbar.js"; export { bindWelcomeMenuControls } from "./chat-room/welcome-menu.js"; export { bindAdminMenuControls } from "./chat-room/admin-menu.js"; +export { + bindBaccaratLossCoverAdminControls, + closeAdminBaccaratLossCoverModal, + closeCurrentBaccaratLossCoverEvent, + loadAdminCurrentLossCoverEvent, + openAdminBaccaratLossCoverModal, + submitBaccaratLossCoverEvent, +} from "./chat-room/baccarat-loss-cover-admin.js"; export { bindFishingControls } from "./chat-room/fishing.js"; export { BLOCKABLE_SYSTEM_SENDERS, @@ -47,6 +55,14 @@ import { bindMobileDrawerControls } from "./chat-room/mobile-drawer.js"; import { bindToolbarControls } from "./chat-room/toolbar.js"; import { bindWelcomeMenuControls } from "./chat-room/welcome-menu.js"; import { bindAdminMenuControls } from "./chat-room/admin-menu.js"; +import { + bindBaccaratLossCoverAdminControls, + closeAdminBaccaratLossCoverModal, + closeCurrentBaccaratLossCoverEvent, + loadAdminCurrentLossCoverEvent, + openAdminBaccaratLossCoverModal, + submitBaccaratLossCoverEvent, +} from "./chat-room/baccarat-loss-cover-admin.js"; import { bindFishingControls } from "./chat-room/fishing.js"; import { BLOCKABLE_SYSTEM_SENDERS, @@ -91,6 +107,12 @@ if (typeof window !== "undefined") { bindToolbarControls, bindWelcomeMenuControls, bindAdminMenuControls, + bindBaccaratLossCoverAdminControls, + closeAdminBaccaratLossCoverModal, + closeCurrentBaccaratLossCoverEvent, + loadAdminCurrentLossCoverEvent, + openAdminBaccaratLossCoverModal, + submitBaccaratLossCoverEvent, bindFishingControls, CHAT_FONT_SIZE_STORAGE_KEY, restoreChatFontSize, @@ -125,6 +147,10 @@ if (typeof window !== "undefined") { window.closeFriendPanel = closeFriendPanel; window.friendSearch = friendSearch; window.openFriendPanel = openFriendPanel; + window.closeAdminBaccaratLossCoverModal = closeAdminBaccaratLossCoverModal; + window.closeCurrentBaccaratLossCoverEvent = closeCurrentBaccaratLossCoverEvent; + window.openAdminBaccaratLossCoverModal = openAdminBaccaratLossCoverModal; + window.submitBaccaratLossCoverEvent = submitBaccaratLossCoverEvent; window.applyFontSize = applyFontSize; // 页面加载后立即注册事件委托,具体业务逻辑仍由各子模块负责。 @@ -134,6 +160,7 @@ if (typeof window !== "undefined") { bindFriendPanelControls(); bindToolbarControls(); bindAdminMenuControls(); + bindBaccaratLossCoverAdminControls(); bindFishingControls(); bindChatRightPanelControls(); bindMobileDrawerControls(); diff --git a/resources/js/chat-room/baccarat-loss-cover-admin.js b/resources/js/chat-room/baccarat-loss-cover-admin.js new file mode 100644 index 0000000..8746d55 --- /dev/null +++ b/resources/js/chat-room/baccarat-loss-cover-admin.js @@ -0,0 +1,288 @@ +// 百乐加强买单活动管理弹层,替代 input-bar 中的内联管理脚本。 + +import { escapeHtml } from "./html.js"; + +let baccaratLossCoverAdminEventsBound = false; + +/** + * 获取买单活动管理弹层元素。 + * + * @returns {HTMLElement|null} + */ +function getModal() { + return document.getElementById("baccarat-loss-cover-admin-modal"); +} + +/** + * 获取 CSRF Token。 + * + * @returns {string} + */ +function csrf() { + return document.querySelector('meta[name="csrf-token"]')?.content || ""; +} + +/** + * 将日期格式化为 datetime-local 输入框需要的值。 + * + * @param {Date} date 日期对象 + * @returns {string} + */ +function formatDateInput(date) { + const year = date.getFullYear(); + const month = String(date.getMonth() + 1).padStart(2, "0"); + const day = String(date.getDate()).padStart(2, "0"); + const hour = String(date.getHours()).padStart(2, "0"); + const minute = String(date.getMinutes()).padStart(2, "0"); + + return `${year}-${month}-${day}T${hour}:${minute}`; +} + +/** + * 读取弹层 data 属性中由 Blade 注入的接口 URL。 + * + * @returns {{summaryUrl:string,storeUrl:string,closeUrlTemplate:string}|null} + */ +function resolveAdminUrls() { + const modal = getModal(); + if (!modal) { + return null; + } + + return { + summaryUrl: modal.dataset.blcSummaryUrl || "", + storeUrl: modal.dataset.blcStoreUrl || "", + closeUrlTemplate: modal.dataset.blcCloseUrlTemplate || "", + }; +} + +/** + * 生成关闭活动接口地址。 + * + * @param {number|string} eventId 活动 ID + * @returns {string} + */ +function resolveCloseUrl(eventId) { + const urls = resolveAdminUrls(); + + return urls?.closeUrlTemplate.replace("__EVENT__", encodeURIComponent(String(eventId))) || ""; +} + +/** + * 安全格式化服务端日期。 + * + * @param {string|null|undefined} value 日期字符串 + * @returns {string} + */ +function formatServerDate(value) { + const date = value ? new Date(value) : null; + + return date && !Number.isNaN(date.getTime()) ? date.toLocaleString("zh-CN") : ""; +} + +/** + * 加载并渲染当前买单活动状态。 + * + * @returns {Promise} + */ +export async function loadAdminCurrentLossCoverEvent() { + const box = document.getElementById("blc-admin-current"); + const urls = resolveAdminUrls(); + if (!box || !urls?.summaryUrl) { + return; + } + + box.innerHTML = "正在加载当前活动…"; + + try { + const response = await fetch(urls.summaryUrl, { + headers: { + Accept: "application/json", + }, + }); + const data = await response.json(); + const event = data.event; + + if (!event) { + box.innerHTML = '
当前没有进行中、待开始或待领取的买单活动。
'; + return; + } + + const canClose = ["scheduled", "active", "settlement_pending"].includes(event.status); + const closeButton = canClose + ? `` + : ""; + + // 服务端字段进入 innerHTML 前统一转义,避免活动标题或开启人污染 DOM。 + box.innerHTML = ` +
+
+
${escapeHtml(String(event.title || ""))}
+
开启人:${escapeHtml(String(event.creator_username || ""))}
+
+ ${escapeHtml(String(event.status_label || ""))} +
+
+ 活动时间:${escapeHtml(formatServerDate(event.starts_at))} - ${escapeHtml(formatServerDate(event.ends_at))}
+ 最终已发补偿:${Number(event.total_claimed_amount || 0).toLocaleString()} 金币 +
+ ${closeButton} + `; + } catch (error) { + box.innerHTML = '
当前活动加载失败,请稍后再试。
'; + } +} + +/** + * 打开管理弹层,并填入默认活动时间。 + * + * @returns {Promise} + */ +export async function openAdminBaccaratLossCoverModal() { + const modal = getModal(); + if (!modal) { + return; + } + + const now = new Date(); + const end = new Date(now.getTime() + 30 * 60 * 1000); + const claimDeadline = new Date(end.getTime() + 24 * 60 * 60 * 1000); + + document.getElementById("blc-admin-starts-at").value = formatDateInput(now); + document.getElementById("blc-admin-ends-at").value = formatDateInput(end); + document.getElementById("blc-admin-claim-deadline-at").value = formatDateInput(claimDeadline); + modal.style.display = "flex"; + + await loadAdminCurrentLossCoverEvent(); +} + +/** + * 关闭管理弹层。 + * + * @returns {void} + */ +export function closeAdminBaccaratLossCoverModal() { + const modal = getModal(); + if (modal) { + modal.style.display = "none"; + } +} + +/** + * 提交创建买单活动请求。 + * + * @param {Event} event 表单提交事件 + * @returns {Promise} + */ +export async function submitBaccaratLossCoverEvent(event) { + event.preventDefault(); + + const urls = resolveAdminUrls(); + if (!urls?.storeUrl) { + return; + } + + const payload = { + title: document.getElementById("blc-admin-title")?.value || "", + description: document.getElementById("blc-admin-description")?.value || "", + starts_at: document.getElementById("blc-admin-starts-at")?.value || "", + ends_at: document.getElementById("blc-admin-ends-at")?.value || "", + claim_deadline_at: document.getElementById("blc-admin-claim-deadline-at")?.value || "", + }; + + try { + const response = await fetch(urls.storeUrl, { + method: "POST", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + "X-CSRF-TOKEN": csrf(), + }, + body: JSON.stringify(payload), + }); + const data = await response.json(); + + if (data.ok) { + await window.chatDialog?.alert(data.message || "活动创建成功", "系统通知", "#16a34a"); + await loadAdminCurrentLossCoverEvent(); + return; + } + + await window.chatDialog?.alert(data.message || "活动创建失败", "提示", "#f59e0b"); + } catch (error) { + await window.chatDialog?.alert("活动创建失败,请稍后重试。", "提示", "#dc2626"); + } +} + +/** + * 关闭当前买单活动。 + * + * @param {number|string} eventId 活动 ID + * @returns {Promise} + */ +export async function closeCurrentBaccaratLossCoverEvent(eventId) { + const closeUrl = resolveCloseUrl(eventId); + if (!closeUrl) { + return; + } + + try { + const response = await fetch(closeUrl, { + method: "POST", + headers: { + Accept: "application/json", + "X-CSRF-TOKEN": csrf(), + }, + }); + const data = await response.json(); + await window.chatDialog?.alert(data.message || "活动状态已更新", "系统通知", "#16a34a"); + await loadAdminCurrentLossCoverEvent(); + } catch (error) { + await window.chatDialog?.alert("活动关闭失败,请稍后重试。", "提示", "#dc2626"); + } +} + +/** + * 绑定买单活动管理弹层事件。 + * + * @returns {void} + */ +export function bindBaccaratLossCoverAdminControls() { + if (baccaratLossCoverAdminEventsBound || typeof document === "undefined") { + return; + } + + baccaratLossCoverAdminEventsBound = true; + document.addEventListener("click", (event) => { + if (!(event.target instanceof Element)) { + return; + } + + if (event.target.closest("[data-blc-admin-close]")) { + event.preventDefault(); + closeAdminBaccaratLossCoverModal(); + return; + } + + const closeCurrentButton = event.target.closest("[data-blc-close-current]"); + if (closeCurrentButton) { + event.preventDefault(); + void closeCurrentBaccaratLossCoverEvent(closeCurrentButton.getAttribute("data-blc-close-current") || ""); + return; + } + + const modal = event.target.closest("#baccarat-loss-cover-admin-modal"); + // 遮罩点击关闭只响应背景层,避免误关内容区。 + if (modal && event.target === modal) { + closeAdminBaccaratLossCoverModal(); + } + }); + + document.addEventListener("submit", (event) => { + if (!(event.target instanceof HTMLFormElement) || !event.target.matches("[data-blc-admin-form]")) { + return; + } + + void submitBaccaratLossCoverEvent(event); + }); +} diff --git a/resources/views/chat/partials/layout/input-bar.blade.php b/resources/views/chat/partials/layout/input-bar.blade.php index 5064390..1a3c6fb 100644 --- a/resources/views/chat/partials/layout/input-bar.blade.php +++ b/resources/views/chat/partials/layout/input-bar.blade.php @@ -327,6 +327,9 @@ $welcomeMessages = [ @if ($canManageLossCover) @@ -346,7 +349,7 @@ $welcomeMessages = [ 正在加载当前活动… -
新建活动
@@ -381,7 +384,7 @@ $welcomeMessages = [
- @@ -394,135 +397,4 @@ $welcomeMessages = [
- - @endif