Files
chatroom/resources/js/chat-room/reward-modal.js
T
2026-04-25 19:00:41 +08:00

245 lines
6.7 KiB
JavaScript

// 聊天室职务奖励金币弹窗组件,负责额度查询、发放提交和全局打开入口。
const EMPTY_REWARD_QUOTA = {
max_once: null,
daily_limit: null,
today_sent: 0,
daily_remaining: null,
recent_rewards: [],
};
/**
* 生成新的空额度对象,避免多个弹窗状态共用同一引用。
*
* @returns {Record<string, any>}
*/
function emptyRewardQuota() {
return {
...EMPTY_REWARD_QUOTA,
recent_rewards: [],
};
}
/**
* 读取 CSRF Token。
*
* @returns {string}
*/
function csrfToken() {
return document.querySelector('meta[name="csrf-token"]')?.content || "";
}
/**
* 格式化本地时间为 MM-DD HH:mm,作为刚发放记录的临时展示时间。
*
* @returns {string}
*/
function currentRewardTimestamp() {
const now = new Date();
const month = String(now.getMonth() + 1).padStart(2, "0");
const day = String(now.getDate()).padStart(2, "0");
const hour = String(now.getHours()).padStart(2, "0");
const minute = String(now.getMinutes()).padStart(2, "0");
return `${month}-${day} ${hour}:${minute}`;
}
/**
* 创建职务奖励金币弹窗 Alpine 组件。
*
* @returns {Record<string, any>}
*/
export function rewardModal() {
return {
show: false,
targetUsername: "",
amount: "",
sending: false,
loading: false,
quota: emptyRewardQuota(),
/**
* 格式化额度数值。
*
* @param {number|null} value 额度值
* @returns {string}
*/
fmt(value) {
if (value === null) {
return "不限";
}
if (value === 0) {
return "—";
}
return `${value.toLocaleString()} 金币`;
},
/**
* 关闭奖励弹窗并同步关闭用户名片弹窗。
*
* @returns {void}
*/
closeRelatedModals() {
this.show = false;
const userModalElement = document.getElementById("user-modal-container");
if (!window.Alpine || !userModalElement) {
return;
}
const userModalData = window.Alpine.$data(userModalElement);
if (userModalData) {
userModalData.showUserModal = false;
}
},
/**
* 打开奖励弹窗并加载当前额度。
*
* @param {string} username 目标用户名
* @returns {Promise<void>}
*/
async open(username) {
this.targetUsername = username;
this.amount = "";
this.sending = false;
this.loading = true;
this.show = true;
try {
const response = await fetch(window.chatContext.rewardQuotaUrl, {
headers: {
Accept: "application/json",
},
});
this.quota = await response.json();
if (!this.quota.recent_rewards) {
this.quota.recent_rewards = [];
}
} catch (error) {
this.quota = emptyRewardQuota();
}
this.loading = false;
},
/**
* 提交奖励金币发放。
*
* @returns {Promise<void>}
*/
async send() {
if (this.sending) {
return;
}
const amount = Number.parseInt(this.amount, 10);
if (!amount || amount <= 0) {
window.chatDialog.alert("请输入有效金额", "提示", "#f59e0b");
return;
}
const maxOnce = window.chatContext?.myMaxReward;
if (maxOnce === 0) {
window.chatDialog.alert("你的职务没有奖励发放权限", "无权限", "#cc4444");
return;
}
if (maxOnce > 0 && amount > maxOnce) {
window.chatDialog.alert(`超出单次上限 ${maxOnce} 金币`, "超出上限", "#cc4444");
return;
}
this.sending = true;
try {
const response = await fetch(window.chatContext.rewardUrl, {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-CSRF-TOKEN": csrfToken(),
Accept: "application/json",
},
body: JSON.stringify({
username: this.targetUsername,
room_id: window.chatContext.roomId,
amount,
}),
});
const data = await response.json();
if (data.status === "success") {
this.recordRewardSuccess(amount);
this.closeRelatedModals();
window.chatDialog.alert(data.message, "🎉 奖励发放成功", "#d97706");
return;
}
window.chatDialog.alert(data.message || "发放失败", "操作失败", "#cc4444");
} catch (error) {
window.chatDialog.alert("网络异常,请稍后重试", "错误", "#cc4444");
}
this.sending = false;
},
/**
* 本地更新额度和最近发放记录。
*
* @param {number} amount 发放金额
* @returns {void}
*/
recordRewardSuccess(amount) {
this.quota.today_sent += amount;
if (this.quota.daily_remaining !== null) {
this.quota.daily_remaining = Math.max(0, this.quota.daily_remaining - amount);
}
this.quota.recent_rewards.unshift({
target: this.targetUsername,
amount,
created_at: currentRewardTimestamp(),
});
if (this.quota.recent_rewards.length > 10) {
this.quota.recent_rewards.pop();
}
this.amount = "";
this.sending = false;
},
};
}
/**
* 打开奖励金币独立弹窗。
*
* @param {string} username
* @returns {void}
*/
export function openRewardModal(username) {
const container = document.getElementById("reward-modal-container");
if (!window.Alpine || !container) {
return;
}
window.Alpine.$data(container)?.open?.(username);
}
/**
* 暴露奖励金币弹窗入口给存量按钮调用。
*
* @returns {void}
*/
export function bindRewardModalControls() {
if (typeof window === "undefined") {
return;
}
window.rewardModal = rewardModal;
window.openRewardModal = openRewardModal;
}