完善猜成语过期与答题记录逻辑

This commit is contained in:
pllx
2026-04-29 10:32:12 +08:00
parent 2f9b2eed64
commit 5962d6d2b3
11 changed files with 685 additions and 115 deletions
+132 -50
View File
@@ -8,6 +8,115 @@ function csrf() {
let currentRoundId = 0;
let currentRoomId = 0;
/**
* 为指定回合创建统一样式的答题按钮。
*/
function buildIdiomAnswerButton(roundId, hint, rewardGold, rewardExp) {
const btn = document.createElement("button");
btn.type = "button";
btn.dataset.idiomAnswerBtn = String(roundId);
btn.dataset.idiomHint = hint;
btn.dataset.idiomGold = String(rewardGold);
btn.dataset.idiomExp = String(rewardExp);
btn.textContent = "🎯 答题";
btn.style.cssText =
"margin-left:8px;padding:2px 12px;background:linear-gradient(135deg,#7c3aed,#a78bfa);" +
"color:#fff;border:none;border-radius:999px;font-size:11px;cursor:pointer;" +
"font-weight:bold;vertical-align:middle;";
return btn;
}
/**
* 清理指定回合的所有答题按钮。
*/
export function removeIdiomAnswerButtons(roundId = 0) {
const selector = roundId > 0
? `[data-idiom-answer-btn="${roundId}"]`
: "[data-idiom-answer-btn]";
document.querySelectorAll(selector).forEach((button) => button.remove());
}
/**
* 把答题按钮挂到对应的消息节点上,而不是盲目追加到最后一条消息。
*/
export function attachIdiomAnswerButton(messageNode, message) {
if (!messageNode || !message) {
return;
}
const roundId = Number.parseInt(
String(message.idiom_game_round_id || message.idom_game_round_id || "0"),
10,
);
if (roundId <= 0) {
return;
}
if (Number.parseInt(String(message.idiom_game_round_ended_id || "0"), 10) > 0) {
return;
}
if (message.from_user !== "星海小博士") {
return;
}
if (messageNode.querySelector(`[data-idiom-answer-btn="${roundId}"]`)) {
return;
}
const hint = String(message.content || "");
const rewardGold = Number.parseInt(String(message.idiom_reward_gold || "0"), 10);
const rewardExp = Number.parseInt(String(message.idiom_reward_exp || "0"), 10);
const button = buildIdiomAnswerButton(roundId, hint, rewardGold, rewardExp);
const timeNode = messageNode.querySelector(".msg-time");
if (timeNode?.parentNode) {
timeNode.parentNode.insertBefore(button, timeNode.nextSibling);
return;
}
messageNode.appendChild(button);
}
/**
* 根据当前服务端回合状态,清理刷新后残留的旧答题按钮。
*/
async function syncCurrentIdiomRound() {
const roomId = Number.parseInt(String(window.chatContext?.roomId || "0"), 10);
if (roomId <= 0) {
return;
}
currentRoomId = roomId;
try {
const response = await fetch(`/idiom-quiz/current?room_id=${roomId}`, {
headers: {
Accept: "application/json",
},
});
const data = await response.json();
const activeRoundId = Number.parseInt(String(data?.data?.round_id || "0"), 10);
currentRoundId = activeRoundId;
if (activeRoundId <= 0) {
removeIdiomAnswerButtons();
return;
}
document.querySelectorAll("[data-idiom-answer-btn]").forEach((button) => {
if (button.dataset.idiomAnswerBtn !== String(activeRoundId)) {
button.remove();
}
});
} catch (_error) {
// 当前回合同步失败时不打断聊天主流程,保留现有按钮状态。
}
}
/**
* 收到猜成语出题事件时,在聊天窗口显示提示消息。
*/
@@ -31,6 +140,7 @@ function handleIdiomGameAnswered(e) {
if (!answer) return;
currentRoundId = 0;
removeIdiomAnswerButtons(round_id);
// 关闭当前用户的答题弹窗(如果开着的话)
const answerModal = document.getElementById("idiom-answer-modal");
@@ -38,35 +148,25 @@ function handleIdiomGameAnswered(e) {
answerModal.style.display = "none";
}
// ── 分屏文字提示 ──
// 回答者 → 包厢(chat-messages-container2
// 其他人 → 公屏(chat-messages-container
// 实时答题结果与刷新后的历史恢复统一走 appendMessage,避免两套分流逻辑跑偏。
const now = new Date();
const timeStr = now.getHours().toString().padStart(2, "0") + ":" +
now.getMinutes().toString().padStart(2, "0") + ":" +
now.getSeconds().toString().padStart(2, "0");
const div = document.createElement("div");
div.className = "msg-line";
div.innerHTML = `<span style="color:#16a34a;font-weight:bold;">🎉 恭喜 <span class="msg-user" data-chat-message-user data-u="${winner_username}" style="color:#16a34a;cursor:pointer;border-bottom:1px dashed #16a34a;">${winner_username}</span> 率先答对成语「${answer}」,获得 ${reward_gold} 金币、${reward_exp} 经验!</span><span class="msg-time">(${timeStr})</span>`;
const isWinner = winner_username === (window.chatContext?.username || "");
if (isWinner) {
// 回答者 → 包厢
const say2 = document.getElementById("chat-messages-container2");
if (say2) {
say2.appendChild(div.cloneNode(true));
say2.scrollTop = say2.scrollHeight;
}
} else {
// 其他人 → 公屏
const say1 = document.getElementById("chat-messages-container");
if (say1) {
say1.appendChild(div);
say1.scrollTop = say1.scrollHeight;
}
}
const timeStr = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, "0")}-${String(now.getDate()).padStart(2, "0")} ${String(now.getHours()).padStart(2, "0")}:${String(now.getMinutes()).padStart(2, "0")}:${String(now.getSeconds()).padStart(2, "0")}`;
window.appendMessage?.({
id: `idiom-result-live-${round_id}-${Date.now()}`,
room_id: currentRoomId || window.chatContext?.roomId || 0,
from_user: "星海小博士",
to_user: "大家",
content: `🎉 【${winner_username}】率先答对成语「${answer}」,获得 ${reward_gold} 金币、${reward_exp} 经验!`,
is_secret: false,
font_color: "#16a34a",
action: "idiom_result",
winner_username,
idiom_answer: answer,
idiom_result_reward_gold: reward_gold,
idiom_result_reward_exp: reward_exp,
idiom_game_round_ended_id: round_id,
sent_at: timeStr,
});
// ── Toast 通知(所有用户都能看到) ──
window.chatToast?.show({
@@ -76,15 +176,6 @@ function handleIdiomGameAnswered(e) {
color: "#16a34a",
duration: 6000,
});
// ── 标记所有对应 round_id 的【答题】按钮为已答 ──
document.querySelectorAll(`[data-idiom-answer-btn="${round_id}"]`).forEach((btn) => {
btn.dataset.idiomAnswered = "1";
btn.textContent = "✅ 已答";
btn.style.background = "#9ca3af";
btn.style.cursor = "default";
btn.style.opacity = "0.6";
});
}
/**
@@ -237,18 +328,6 @@ export function bindIdiomQuizControls() {
const btn = e.target.closest("[data-idiom-answer-btn]");
if (!btn) return;
// 已答完的按钮不可点击
if (btn.dataset.idiomAnswered === "1") {
window.chatToast?.show({
title: "🧩 猜成语",
message: "这道题已被答过了,等下一题吧!",
icon: "😅",
color: "#9ca3af",
duration: 3000,
});
return;
}
const roundId = parseInt(btn.dataset.idiomAnswerBtn || "0", 10);
const hint = btn.dataset.idiomHint || "";
const rewardGold = parseInt(btn.dataset.idiomGold || "0", 10);
@@ -261,7 +340,10 @@ export function bindIdiomQuizControls() {
// ── 猜成语结果消息中的用户名可点击 → 打开用户名片
// 注:单击/双击已由 right-panel.js 的全局 [data-chat-message-user] 事件委托统一处理
window.setTimeout(() => {
syncCurrentIdiomRound();
}, 0);
}
// ── 挂载到 window ──