Files
chatroom/resources/js/chat-room/idiom-quiz.js
T
pllx 4ff62e29bd feat: 猜成语游戏 - 完整题库、管理后台、答题弹窗
- 创建 idioms 表(102条谜语式成语题库)和 idiom_game_rounds 表
- 后台成语管理页面:增删改题目 + 游戏参数(金币/经验/间隔)内联设置 + 出题按钮
- IdiomQuizController:出题/答题/当前回合查询,Redis 防并发抢答
- IdiomGameStarted / IdiomGameAnswered 广播事件
- 前端答题弹窗模块:聊天消息带【答题】按钮,点击弹出输入框
- GameConfig 注册 idiom 游戏,由 admin.game-configs 统一管理开关
2026-04-28 23:42:48 +08:00

216 lines
6.7 KiB
JavaScript

// 猜成语游戏前端模块
// 监听 IdiomGameStarted / IdiomGameAnswered 事件,提供答题弹窗功能
function csrf() {
return document.querySelector('meta[name="csrf-token"]')?.content ?? "";
}
let currentRoundId = 0;
let currentRoomId = 0;
/**
* 收到猜成语出题事件时,在聊天窗口显示提示消息。
*/
function handleIdiomGameStarted(e) {
const { round_id, hint, reward_gold, reward_exp, message } = e.detail || {};
if (!round_id || !hint) return;
currentRoundId = round_id;
currentRoomId = window.chatContext?.roomId || 0;
// 追加一条聊天室消息(由 MessageSent 事件负责渲染,不重复添加)
// 这里只存储当前回合信息
console.log(`猜成语开始:${hint},奖励 ${reward_gold}金/${reward_exp}经验`);
}
/**
* 收到猜成语结果事件。
*/
function handleIdiomGameAnswered(e) {
const { answer, winner_username, reward_gold, reward_exp } = e.detail || {};
if (!answer) return;
currentRoundId = 0;
// 如果当前用户打开答题弹窗但被别人抢先了,关闭弹窗
const answerModal = document.getElementById("idiom-answer-modal");
if (answerModal && answerModal.style.display !== "none") {
answerModal.style.display = "none";
window.chatToast?.show({
title: "被抢先了",
message: `${winner_username} 率先答对了「${answer}」,下次加油!`,
icon: "😅",
color: "#f59e0b",
duration: 4000,
});
}
}
/**
* 打开答题弹窗。
*/
function openIdiomAnswerModal(roundId, hint, rewardGold, rewardExp) {
currentRoundId = roundId;
currentRoomId = window.chatContext?.roomId || 0;
const modal = document.getElementById("idiom-answer-modal");
if (!modal) return;
const hintEl = document.getElementById("idiom-answer-hint");
const rewardEl = document.getElementById("idiom-answer-reward");
if (hintEl) hintEl.textContent = hint;
if (rewardEl) rewardEl.textContent = `🎁 答对奖励:${rewardGold} 金币 + ${rewardExp} 经验`;
modal.style.display = "flex";
const input = document.getElementById("idiom-answer-input");
if (input) {
input.value = "";
input.focus();
input.disabled = false;
}
const submitBtn = document.getElementById("idiom-answer-submit");
if (submitBtn) {
submitBtn.disabled = false;
submitBtn.textContent = "提交答案";
}
const feedbackEl = document.getElementById("idiom-answer-feedback");
if (feedbackEl) feedbackEl.textContent = "";
}
/**
* 关闭答题弹窗。
*/
function closeIdiomAnswerModal() {
const modal = document.getElementById("idiom-answer-modal");
if (modal) modal.style.display = "none";
}
/**
* 提交答案。
*/
async function submitIdiomAnswer() {
const input = document.getElementById("idiom-answer-input");
const feedbackEl = document.getElementById("idiom-answer-feedback");
const submitBtn = document.getElementById("idiom-answer-submit");
if (!input || !feedbackEl || !submitBtn) return;
const answer = input.value.trim();
if (!answer) {
feedbackEl.textContent = "请输入成语答案";
feedbackEl.style.color = "#ef4444";
return;
}
submitBtn.disabled = true;
submitBtn.textContent = "提交中...";
try {
const response = await fetch("/idiom-quiz/answer", {
method: "POST",
headers: {
"X-CSRF-TOKEN": csrf(),
"Content-Type": "application/json",
"Accept": "application/json",
},
body: JSON.stringify({
round_id: currentRoundId,
answer: answer,
room_id: currentRoomId,
}),
});
const data = await response.json();
if (data.status === "success") {
feedbackEl.textContent = data.message || "🎉 回答正确!";
feedbackEl.style.color = "#16a34a";
input.disabled = true;
// 延迟关闭弹窗
setTimeout(() => {
closeIdiomAnswerModal();
}, 2000);
} else {
feedbackEl.textContent = data.message || "答案不正确";
feedbackEl.style.color = "#ef4444";
submitBtn.disabled = false;
submitBtn.textContent = "提交答案";
input.focus();
input.select();
}
} catch (error) {
feedbackEl.textContent = "网络错误,请稍后重试";
feedbackEl.style.color = "#ef4444";
submitBtn.disabled = false;
submitBtn.textContent = "提交答案";
}
}
// ── 事件绑定 ──
export function bindIdiomQuizControls() {
// 已经绑定的不再重复绑定
if (document.getElementById("idiom-answer-modal")?.dataset?.idiomBound) return;
const modal = document.getElementById("idiom-answer-modal");
if (modal) modal.dataset.idiomBound = "1";
// 关闭按钮
document.addEventListener("click", (e) => {
const closeBtn = e.target.closest("[data-idiom-answer-close]");
if (closeBtn) {
closeIdiomAnswerModal();
return;
}
// 点击遮罩层关闭
const overlay = e.target.closest("#idiom-answer-modal");
if (overlay && e.target === overlay) {
closeIdiomAnswerModal();
}
});
// 提交按钮
document.addEventListener("click", (e) => {
const submitBtn = e.target.closest("[data-idiom-answer-submit]");
if (submitBtn) {
e.preventDefault();
submitIdiomAnswer();
}
});
// 输入框 Enter 提交
document.addEventListener("keydown", (e) => {
const input = e.target.closest("#idiom-answer-input");
if (input && e.key === "Enter" && !e.shiftKey) {
e.preventDefault();
submitIdiomAnswer();
}
});
// 聊天消息中的【答题】按钮点击
document.addEventListener("click", (e) => {
const btn = e.target.closest("[data-idiom-answer-btn]");
if (!btn) return;
const roundId = parseInt(btn.dataset.idiomAnswerBtn || "0", 10);
const hint = btn.dataset.idiomHint || "";
const rewardGold = parseInt(btn.dataset.idiomGold || "0", 10);
const rewardExp = parseInt(btn.dataset.idiomExp || "0", 10);
if (roundId > 0) {
openIdiomAnswerModal(roundId, hint, rewardGold, rewardExp);
}
});
}
// ── 挂载到 window ──
window.openIdiomAnswerModal = openIdiomAnswerModal;
window.closeIdiomAnswerModal = closeIdiomAnswerModal;
window.submitIdiomAnswer = submitIdiomAnswer;
window.handleIdiomGameStarted = handleIdiomGameStarted;
window.handleIdiomGameAnswered = handleIdiomGameAnswered;