迁移游戏配置手动操作

This commit is contained in:
2026-04-25 13:23:20 +08:00
parent 85a419fe53
commit 4751a5578e
2 changed files with 245 additions and 152 deletions
+224
View File
@@ -2,6 +2,12 @@
let adminGameConfigControlsBound = false;
const MYSTERY_BOX_META = {
normal: { name: "普通箱", icon: "📦" },
rare: { name: "稀有箱", icon: "💎" },
trap: { name: "黑化箱", icon: "☠️" },
};
/**
* 读取后台 layout 注入的 CSRF token。
*
@@ -221,6 +227,174 @@ async function toggleGame(button) {
}
}
/**
* 管理员手动投放神秘箱子。
*
* @param {HTMLButtonElement} button 投放按钮
* @returns {void}
*/
function dropMysteryBox(button) {
const boxType = button.getAttribute("data-game-drop-box-type") || "normal";
const dropUrl = button.getAttribute("data-game-drop-box-url") || "";
const meta = MYSTERY_BOX_META[boxType] || MYSTERY_BOX_META.normal;
if (!dropUrl) {
return;
}
window.adminDialog?.confirm(
`确定要向 <b>#1 房间</b> 投放一个「${meta.name}」吗?<br><span style="color:#64748b; font-size:12px;">箱子投放后将立即在公屏广播暗号,用户限时领取。</span>`,
`投放${meta.name}`,
() => {
fetch(dropUrl, {
method: "POST",
headers: {
"X-CSRF-TOKEN": getCsrfToken(),
"Content-Type": "application/json",
"Accept": "application/json",
},
body: JSON.stringify({ box_type: boxType }),
})
.then((response) => response.json())
.then((data) => {
window.adminDialog?.alert(
data.message || (data.ok ? "投放成功!" : "投放失败"),
data.ok ? "投放成功" : "投放失败",
data.ok ? meta.icon : "❌",
);
})
.catch(() => window.adminDialog?.alert("网络错误,请重试", "网络错误", "🌐"));
},
meta.icon,
);
}
/**
* 加载双色球当前期次状态。
*
* @param {HTMLButtonElement} button 状态加载按钮
* @returns {Promise<void>}
*/
async function loadLotteryStatus(button) {
const currentUrl = button.getAttribute("data-game-lottery-current-url") || "";
const statusPanel = document.getElementById("lottery-issue-status");
if (!currentUrl || !statusPanel) {
return;
}
statusPanel.innerHTML = '<span class="text-gray-400">⏳ 加载中…</span>';
try {
const response = await fetch(currentUrl, {
headers: { "Accept": "application/json" },
});
const data = await response.json();
if (!data.issue) {
statusPanel.innerHTML = '<span class="text-orange-500">⚠️ 当前无进行中期次,可手动开新期</span>';
return;
}
const issue = data.issue;
const pool = Number(issue.pool_amount).toLocaleString();
const statusMap = {
open: "🟢 购票中",
closed: "🔴 已停售",
settled: "✅ 已开奖",
};
const superTag = issue.is_super_issue ? " 🎊<b>超级期</b>" : "";
const drawAt = issue.draw_at ? issue.draw_at.replace("T", " ") : "--";
statusPanel.innerHTML = `第 <b>${issue.issue_no}</b> 期${superTag} &nbsp;·&nbsp; 状态:${statusMap[issue.status] || issue.status}
&nbsp;·&nbsp; 奖池:<b style="color:#dc2626">💰 ${pool} 金币</b>
&nbsp;·&nbsp; 预计开奖:${drawAt}
&nbsp;·&nbsp; 已购:${issue.total_tickets || 0}`;
} catch (error) {
statusPanel.innerHTML = '<span class="text-red-400">❌ 加载失败</span>';
}
}
/**
* 手动开启新一期双色球。
*
* @param {HTMLButtonElement} button 开期按钮
* @returns {void}
*/
function openLotteryIssue(button) {
const openUrl = button.getAttribute("data-game-lottery-open-url") || "";
if (!openUrl) {
return;
}
window.adminDialog?.confirm(
'确定要手动开启新一期双色球吗?<br><span style="color:#64748b;font-size:12px;">仅在无进行中期次时生效,开奖时间将使用当前配置的 draw_hour:draw_minute。</span>',
"手动开新期",
() => {
fetch(openUrl, {
method: "POST",
headers: {
"X-CSRF-TOKEN": getCsrfToken(),
"Accept": "application/json",
},
})
.then((response) => response.json())
.then((data) => {
window.adminDialog?.alert(data.message, data.ok ? "操作成功" : "操作失败", data.ok ? "✅" : "❌");
if (data.ok) {
const statusButton = document.querySelector("[data-game-lottery-current-url]");
if (statusButton instanceof HTMLButtonElement) {
void loadLotteryStatus(statusButton);
}
}
})
.catch(() => window.adminDialog?.alert("网络错误,请重试", "网络错误", "🌐"));
},
"",
);
}
/**
* 强制提前开奖,用于测试或管理需要。
*
* @param {HTMLButtonElement} button 强制开奖按钮
* @returns {void}
*/
function forceLotteryDraw(button) {
const drawUrl = button.getAttribute("data-game-lottery-force-url") || "";
if (!drawUrl) {
return;
}
window.adminDialog?.confirm(
'确定要<b style="color:red">立即强制开奖</b>吗?<br><span style="color:#64748b;font-size:12px;">将对当前 closed 或 open 期次立即执行开奖,此操作不可撤销!</span>',
"强制开奖确认",
() => {
fetch(drawUrl, {
method: "POST",
headers: {
"X-CSRF-TOKEN": getCsrfToken(),
"Accept": "application/json",
},
})
.then((response) => response.json())
.then((data) => {
window.adminDialog?.alert(data.message, data.ok ? "开奖完成" : "操作失败", data.ok ? "🎊" : "❌");
if (data.ok) {
const statusButton = document.querySelector("[data-game-lottery-current-url]");
if (statusButton instanceof HTMLButtonElement) {
void loadLotteryStatus(statusButton);
}
}
})
.catch(() => window.adminDialog?.alert("网络错误,请重试", "网络错误", "🌐"));
},
"🎊",
);
}
/**
* 绑定游戏管理页通用操作按钮。
*
@@ -248,6 +422,56 @@ export function bindAdminGameConfigControls() {
if (toggleButton instanceof HTMLButtonElement) {
event.preventDefault();
void toggleGame(toggleButton);
return;
}
const dropBoxButton = event.target.closest("[data-game-drop-box-type]");
if (dropBoxButton instanceof HTMLButtonElement) {
event.preventDefault();
dropMysteryBox(dropBoxButton);
return;
}
const lotteryStatusButton = event.target.closest("[data-game-lottery-current-url]");
if (lotteryStatusButton instanceof HTMLButtonElement) {
event.preventDefault();
void loadLotteryStatus(lotteryStatusButton);
return;
}
const lotteryOpenButton = event.target.closest("[data-game-lottery-open-url]");
if (lotteryOpenButton instanceof HTMLButtonElement) {
event.preventDefault();
openLotteryIssue(lotteryOpenButton);
return;
}
const lotteryForceButton = event.target.closest("[data-game-lottery-force-url]");
if (lotteryForceButton instanceof HTMLButtonElement) {
event.preventDefault();
forceLotteryDraw(lotteryForceButton);
}
});
document.addEventListener("mouseover", (event) => {
if (!(event.target instanceof Element)) {
return;
}
const hoverButton = event.target.closest("[data-game-hover-opacity]");
if (hoverButton instanceof HTMLElement) {
hoverButton.style.opacity = hoverButton.getAttribute("data-game-hover-opacity") || "";
}
});
document.addEventListener("mouseout", (event) => {
if (!(event.target instanceof Element)) {
return;
}
const hoverButton = event.target.closest("[data-game-hover-opacity]");
if (hoverButton instanceof HTMLElement) {
hoverButton.style.opacity = "1";
}
});
}