174 lines
5.0 KiB
JavaScript
174 lines
5.0 KiB
JavaScript
// 聊天室神秘占卜游戏面板,提供 fortunePanel Alpine 组件和空闲初始化。
|
|
|
|
/**
|
|
* 读取 CSRF Token。
|
|
*
|
|
* @returns {string}
|
|
*/
|
|
function csrf() {
|
|
return document.querySelector("meta[name=csrf-token]")?.content || "";
|
|
}
|
|
|
|
/**
|
|
* 创建神秘占卜面板 Alpine 组件。
|
|
*
|
|
* @returns {object}
|
|
*/
|
|
export function fortunePanel() {
|
|
return {
|
|
show: false,
|
|
activeTab: "tell",
|
|
loading: false,
|
|
shaking: false,
|
|
freeCount: 1,
|
|
freeUsed: 0,
|
|
hasFreeLeft: true,
|
|
extraCost: 500,
|
|
todayLatest: null,
|
|
resultGrade: "",
|
|
resultLabel: "",
|
|
resultColor: "#a855f7",
|
|
resultText: "",
|
|
resultBuff: null,
|
|
historyLogs: [],
|
|
|
|
/**
|
|
* 加载今日占卜状态。
|
|
*
|
|
* @returns {Promise<void>}
|
|
*/
|
|
async loadTodayStatus() {
|
|
try {
|
|
const response = await fetch("/fortune/today");
|
|
const data = await response.json();
|
|
if (!data.enabled) {
|
|
return;
|
|
}
|
|
|
|
this.freeCount = data.free_count || 1;
|
|
this.freeUsed = data.free_used || 0;
|
|
this.hasFreeLeft = data.has_free_left ?? true;
|
|
this.extraCost = data.extra_cost || 500;
|
|
this.todayLatest = data.latest || null;
|
|
} catch (error) {
|
|
// 占卜状态失败不影响聊天室主流程。
|
|
}
|
|
},
|
|
|
|
/**
|
|
* 执行一次占卜。
|
|
*
|
|
* @returns {Promise<void>}
|
|
*/
|
|
async doFortune() {
|
|
if (this.loading) {
|
|
return;
|
|
}
|
|
|
|
this.loading = true;
|
|
this.shaking = true;
|
|
|
|
// 摇卦动画先播放,再请求服务端结果,保持旧版节奏。
|
|
await new Promise((resolve) => window.setTimeout(resolve, 600));
|
|
this.shaking = false;
|
|
|
|
try {
|
|
const response = await fetch("/fortune/tell", {
|
|
method: "POST",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
"Accept": "application/json",
|
|
"X-CSRF-TOKEN": csrf(),
|
|
},
|
|
});
|
|
const data = await response.json();
|
|
|
|
if (data.ok) {
|
|
this.resultGrade = data.grade;
|
|
this.resultLabel = data.grade_label;
|
|
this.resultColor = data.grade_color;
|
|
this.resultText = data.text;
|
|
this.resultBuff = data.buff_desc;
|
|
|
|
// 免费次数只在服务端确认免费占卜成功后本地递增。
|
|
if (data.is_free) {
|
|
this.freeUsed += 1;
|
|
this.hasFreeLeft = this.freeUsed < this.freeCount;
|
|
}
|
|
return;
|
|
}
|
|
|
|
window.chatDialog?.alert?.(data.message || "占卜失败", "提示", "#ef4444");
|
|
} catch (error) {
|
|
window.chatDialog?.alert?.("网络异常,请稍后重试。", "错误", "#ef4444");
|
|
} finally {
|
|
this.loading = false;
|
|
}
|
|
},
|
|
|
|
/**
|
|
* 加载占卜历史记录。
|
|
*
|
|
* @returns {Promise<void>}
|
|
*/
|
|
async loadHistory() {
|
|
if (this.historyLogs.length > 0) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const response = await fetch("/fortune/history");
|
|
const data = await response.json();
|
|
this.historyLogs = data.history || [];
|
|
} catch (error) {
|
|
this.historyLogs = [];
|
|
}
|
|
},
|
|
};
|
|
}
|
|
|
|
/**
|
|
* 空闲时预加载今日占卜状态。
|
|
*
|
|
* @returns {void}
|
|
*/
|
|
function scheduleFortuneBootstrap() {
|
|
document.addEventListener("DOMContentLoaded", () => window.deferChatGameBootstrap?.(async () => {
|
|
try {
|
|
const response = await fetch("/fortune/today");
|
|
const data = await response.json();
|
|
if (!data.enabled) {
|
|
return;
|
|
}
|
|
|
|
const panel = document.getElementById("fortune-panel");
|
|
if (!panel || !window.Alpine) {
|
|
return;
|
|
}
|
|
|
|
const panelData = window.Alpine.$data(panel);
|
|
panelData.freeCount = data.free_count || 1;
|
|
panelData.freeUsed = data.free_used || 0;
|
|
panelData.hasFreeLeft = data.has_free_left ?? true;
|
|
panelData.extraCost = data.extra_cost || 500;
|
|
panelData.todayLatest = data.latest || null;
|
|
} catch (error) {
|
|
console.warn("[神秘占卜] 初始化失败", error);
|
|
}
|
|
}));
|
|
}
|
|
|
|
/**
|
|
* 绑定神秘占卜全局 Alpine 工厂函数。
|
|
*
|
|
* @returns {void}
|
|
*/
|
|
export function bindFortunePanelControls() {
|
|
if (typeof window === "undefined") {
|
|
return;
|
|
}
|
|
|
|
window.fortunePanel = fortunePanel;
|
|
scheduleFortuneBootstrap();
|
|
}
|