From 85a419fe538d7f47dfc70734e3bb77317c803fd8 Mon Sep 17 00:00:00 2001 From: lkddi Date: Sat, 25 Apr 2026 13:21:45 +0800 Subject: [PATCH] =?UTF-8?q?=E8=BF=81=E7=A7=BB=E6=B8=B8=E6=88=8F=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=E7=BB=9F=E8=AE=A1=E4=B8=8E=E5=BC=80=E5=85=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- resources/js/admin/game-configs.js | 253 ++++++++++++++++++ resources/js/app.js | 2 + .../views/admin/game-configs/index.blade.php | 217 +-------------- 3 files changed, 258 insertions(+), 214 deletions(-) create mode 100644 resources/js/admin/game-configs.js diff --git a/resources/js/admin/game-configs.js b/resources/js/admin/game-configs.js new file mode 100644 index 0000000..530d249 --- /dev/null +++ b/resources/js/admin/game-configs.js @@ -0,0 +1,253 @@ +// 游戏管理后台事件代理,逐步替代 game-configs Blade 内联脚本。 + +let adminGameConfigControlsBound = false; + +/** + * 读取后台 layout 注入的 CSRF token。 + * + * @returns {string} + */ +function getCsrfToken() { + return document.querySelector('meta[name="csrf-token"]')?.getAttribute("content") || ""; +} + +/** + * 生成单个统计卡片 HTML。 + * + * @param {{icon:string,title:string,color:string,items:Array<{label:string,value:string|number}>}} card 统计卡片 + * @returns {string} + */ +function renderStatsCard(card) { + return ` +
+
+ ${card.icon} + ${card.title} +
+
+ ${card.items.map((item) => ` +
+ ${item.label} + ${item.value} +
+ `).join("")} +
+
+ `; +} + +/** + * 将游戏统计接口响应转换为顶部统计卡片。 + * + * @param {Record>} data 统计接口响应 + * @returns {string} + */ +function renderGameStats(data) { + const cards = [ + { + icon: "🎲", + title: "百家乐", + items: [ + { label: "总局数", value: data.baccarat.total_rounds.toLocaleString() }, + { label: "总下注", value: `${data.baccarat.total_bets.toLocaleString()} 笔` }, + { label: "今日局数", value: data.baccarat.today_rounds.toLocaleString() }, + ], + color: "border-red-200 bg-red-50", + }, + { + icon: "🎰", + title: "老虎机", + items: [ + { label: "总转动", value: `${data.slot.total_spins.toLocaleString()} 次` }, + { label: "三7大奖", value: `${data.slot.jackpot_count.toLocaleString()} 次` }, + { label: "今日转动", value: data.slot.today_spins.toLocaleString() }, + ], + color: "border-amber-200 bg-amber-50", + }, + { + icon: "🐎", + title: "赛马竞猜", + items: [ + { label: "总场次", value: data.horse.total_races.toLocaleString() }, + { label: "总注池", value: `${data.horse.total_pool.toLocaleString()} 金` }, + { label: "今日场次", value: data.horse.today_races.toLocaleString() }, + ], + color: "border-emerald-200 bg-emerald-50", + }, + { + icon: "📦", + title: "神秘箱子", + items: [ + { label: "总投放", value: data.mystery_box.total_dropped.toLocaleString() }, + { label: "已领取", value: data.mystery_box.total_claimed.toLocaleString() }, + { label: "今日投放", value: data.mystery_box.today_dropped.toLocaleString() }, + ], + color: "border-purple-200 bg-purple-50", + }, + { + icon: "🔮", + title: "神秘占卜", + items: [ + { label: "总占卜", value: `${data.fortune.total_times.toLocaleString()} 次` }, + { label: "吉签/凶签", value: `${data.fortune.jackpot_count} / ${data.fortune.curse_count}` }, + { label: "今日占卜", value: data.fortune.today_times.toLocaleString() }, + ], + color: "border-fuchsia-200 bg-fuchsia-50", + }, + { + icon: "🎟️", + title: "双色球彩票", + items: [ + { label: "总期数", value: `${data.lottery.total_issues.toLocaleString()} 期` }, + { label: "历史彩票", value: `${data.lottery.total_bets.toLocaleString()} 张` }, + { label: "累计奖池", value: `${data.lottery.total_pool.toLocaleString()} 金` }, + ], + color: "border-rose-200 bg-rose-50", + }, + { + icon: "♟️", + title: "五子棋", + items: [ + { label: "总对局", value: `${data.gomoku.total_games.toLocaleString()} 局` }, + { label: "人机/对战", value: `${data.gomoku.pve_count} / ${data.gomoku.pvp_count}` }, + { label: "今日对局", value: data.gomoku.today_games.toLocaleString() }, + ], + color: "border-blue-200 bg-blue-50", + }, + ]; + + return cards.map((card) => renderStatsCard(card)).join(""); +} + +/** + * 加载各游戏实时统计摘要并渲染到顶部面板。 + * + * @param {HTMLButtonElement} button 触发按钮 + * @returns {Promise} + */ +async function loadGameStats(button) { + const statsUrl = button.getAttribute("data-game-stats-url") || ""; + const panel = document.getElementById("game-stats-panel"); + const grid = document.getElementById("game-stats-grid"); + + if (!statsUrl || !panel || !grid) { + return; + } + + grid.innerHTML = '
⏳ 加载中...
'; + panel.classList.remove("hidden"); + + try { + const response = await fetch(statsUrl, { + headers: { "Accept": "application/json" }, + }); + const data = await response.json(); + + grid.innerHTML = renderGameStats(data); + } catch (error) { + grid.innerHTML = '
❌ 加载失败,请重试
'; + } +} + +/** + * 根据开关接口响应更新游戏卡片状态。 + * + * @param {HTMLButtonElement} button 开关按钮 + * @param {boolean} enabled 是否启用 + * @returns {void} + */ +function updateGameToggleState(button, enabled) { + const gameKey = button.getAttribute("data-game-key") || ""; + const card = document.getElementById(`game-card-${gameKey}`); + const badge = document.getElementById(`badge-${gameKey}`); + const header = card?.querySelector(".flex.items-center.justify-between"); + + if (badge) { + badge.textContent = enabled ? "运行中" : "已关闭"; + badge.className = `text-xs px-2 py-0.5 rounded-full font-bold ${ + enabled ? "bg-emerald-100 text-emerald-700" : "bg-gray-200 text-gray-500" + }`; + } + + button.textContent = enabled ? "⏸ 关闭游戏" : "▶ 开启游戏"; + button.className = `px-5 py-2 rounded-lg font-bold text-sm transition shadow-sm ${ + enabled ? "bg-red-500 hover:bg-red-600 text-white" : "bg-emerald-500 hover:bg-emerald-600 text-white" + }`; + + // 卡片头部背景是游戏状态的主要视觉反馈,需要和按钮、徽章同步。 + if (header) { + header.classList.toggle("bg-emerald-50", enabled); + header.classList.toggle("bg-gray-50", !enabled); + } +} + +/** + * 切换游戏开启/关闭状态。 + * + * @param {HTMLButtonElement} button 开关按钮 + * @returns {Promise} + */ +async function toggleGame(button) { + const toggleUrl = button.getAttribute("data-game-toggle-url") || ""; + + if (!toggleUrl || button.disabled) { + return; + } + + button.disabled = true; + + try { + const response = await fetch(toggleUrl, { + method: "POST", + headers: { + "X-CSRF-TOKEN": getCsrfToken(), + "Accept": "application/json", + }, + }); + const data = await response.json(); + + if (!data?.ok) { + return; + } + + updateGameToggleState(button, Boolean(data.enabled)); + window.adminDialog?.alert( + data.message, + data.enabled ? "游戏已开启" : "游戏已关闭", + data.enabled ? "✅" : "⏸", + ); + } finally { + button.disabled = false; + } +} + +/** + * 绑定游戏管理页通用操作按钮。 + * + * @returns {void} + */ +export function bindAdminGameConfigControls() { + if (adminGameConfigControlsBound || typeof document === "undefined") { + return; + } + + adminGameConfigControlsBound = true; + document.addEventListener("click", (event) => { + if (!(event.target instanceof Element)) { + return; + } + + const statsButton = event.target.closest("[data-game-stats-url]"); + if (statsButton instanceof HTMLButtonElement) { + event.preventDefault(); + void loadGameStats(statsButton); + return; + } + + const toggleButton = event.target.closest("[data-game-toggle-url]"); + if (toggleButton instanceof HTMLButtonElement) { + event.preventDefault(); + void toggleGame(toggleButton); + } + }); +} diff --git a/resources/js/app.js b/resources/js/app.js index a7fcbdc..506a854 100644 --- a/resources/js/app.js +++ b/resources/js/app.js @@ -1,9 +1,11 @@ import './bootstrap'; import { bindAdminAutoactControls } from './admin/autoact.js'; import { bindAdminFishingEventsControls } from './admin/fishing-events.js'; +import { bindAdminGameConfigControls } from './admin/game-configs.js'; import { bindAdminSignInRulesControls } from './admin/sign-in-rules.js'; // 后台共用入口只注册轻量事件代理,具体页面通过 data-* 属性决定是否响应。 bindAdminAutoactControls(); bindAdminFishingEventsControls(); +bindAdminGameConfigControls(); bindAdminSignInRulesControls(); diff --git a/resources/views/admin/game-configs/index.blade.php b/resources/views/admin/game-configs/index.blade.php index 73a687f..a7ac5ab 100644 --- a/resources/views/admin/game-configs/index.blade.php +++ b/resources/views/admin/game-configs/index.blade.php @@ -11,7 +11,7 @@

🎮 游戏管理

统一管理聊天室所有娱乐游戏的开关状态与核心参数,所有游戏默认关闭。

- @@ -70,7 +70,8 @@ @endif {{-- 大开关按钮 --}} -