迁移银行弹窗业务脚本
This commit is contained in:
@@ -20,7 +20,17 @@ export {
|
|||||||
openAdminBaccaratLossCoverModal,
|
openAdminBaccaratLossCoverModal,
|
||||||
submitBaccaratLossCoverEvent,
|
submitBaccaratLossCoverEvent,
|
||||||
} from "./chat-room/baccarat-loss-cover-admin.js";
|
} from "./chat-room/baccarat-loss-cover-admin.js";
|
||||||
export { bindBankControls } from "./chat-room/bank-controls.js";
|
export {
|
||||||
|
bankAction,
|
||||||
|
bankLoadInfo,
|
||||||
|
bankShowMsg,
|
||||||
|
bindBankControls,
|
||||||
|
closeBankModal,
|
||||||
|
fetchBankRanking,
|
||||||
|
openBankModal,
|
||||||
|
switchBankTab,
|
||||||
|
toggleBankRankSort,
|
||||||
|
} from "./chat-room/bank-modal.js";
|
||||||
export { bindFishingControls } from "./chat-room/fishing.js";
|
export { bindFishingControls } from "./chat-room/fishing.js";
|
||||||
export { bindProfileControls } from "./chat-room/profile-controls.js";
|
export { bindProfileControls } from "./chat-room/profile-controls.js";
|
||||||
export { bindShopControls } from "./chat-room/shop-controls.js";
|
export { bindShopControls } from "./chat-room/shop-controls.js";
|
||||||
@@ -69,7 +79,17 @@ import {
|
|||||||
openAdminBaccaratLossCoverModal,
|
openAdminBaccaratLossCoverModal,
|
||||||
submitBaccaratLossCoverEvent,
|
submitBaccaratLossCoverEvent,
|
||||||
} from "./chat-room/baccarat-loss-cover-admin.js";
|
} from "./chat-room/baccarat-loss-cover-admin.js";
|
||||||
import { bindBankControls } from "./chat-room/bank-controls.js";
|
import {
|
||||||
|
bankAction,
|
||||||
|
bankLoadInfo,
|
||||||
|
bankShowMsg,
|
||||||
|
bindBankControls,
|
||||||
|
closeBankModal,
|
||||||
|
fetchBankRanking,
|
||||||
|
openBankModal,
|
||||||
|
switchBankTab,
|
||||||
|
toggleBankRankSort,
|
||||||
|
} from "./chat-room/bank-modal.js";
|
||||||
import { bindFishingControls } from "./chat-room/fishing.js";
|
import { bindFishingControls } from "./chat-room/fishing.js";
|
||||||
import { bindProfileControls } from "./chat-room/profile-controls.js";
|
import { bindProfileControls } from "./chat-room/profile-controls.js";
|
||||||
import { bindShopControls } from "./chat-room/shop-controls.js";
|
import { bindShopControls } from "./chat-room/shop-controls.js";
|
||||||
@@ -100,7 +120,7 @@ import {
|
|||||||
import { createMessageQueue } from "./chat-room/message-queue.js";
|
import { createMessageQueue } from "./chat-room/message-queue.js";
|
||||||
|
|
||||||
if (typeof window !== "undefined") {
|
if (typeof window !== "undefined") {
|
||||||
// 保留聚合入口,给仍在 Blade 内的存量脚本按需读取已迁移工具。
|
// 保留聚合入口,给新迁移模块、测试和仍在 Blade 内的存量脚本统一读取工具。
|
||||||
window.ChatRoomTools = {
|
window.ChatRoomTools = {
|
||||||
escapeHtml,
|
escapeHtml,
|
||||||
escapeHtmlWithLineBreaks,
|
escapeHtmlWithLineBreaks,
|
||||||
@@ -123,7 +143,15 @@ if (typeof window !== "undefined") {
|
|||||||
loadAdminCurrentLossCoverEvent,
|
loadAdminCurrentLossCoverEvent,
|
||||||
openAdminBaccaratLossCoverModal,
|
openAdminBaccaratLossCoverModal,
|
||||||
submitBaccaratLossCoverEvent,
|
submitBaccaratLossCoverEvent,
|
||||||
|
bankAction,
|
||||||
|
bankLoadInfo,
|
||||||
|
bankShowMsg,
|
||||||
bindBankControls,
|
bindBankControls,
|
||||||
|
closeBankModal,
|
||||||
|
fetchBankRanking,
|
||||||
|
openBankModal,
|
||||||
|
switchBankTab,
|
||||||
|
toggleBankRankSort,
|
||||||
bindFishingControls,
|
bindFishingControls,
|
||||||
bindMarriageStatusControls,
|
bindMarriageStatusControls,
|
||||||
bindProfileControls,
|
bindProfileControls,
|
||||||
@@ -156,7 +184,7 @@ if (typeof window !== "undefined") {
|
|||||||
createMessageQueue,
|
createMessageQueue,
|
||||||
};
|
};
|
||||||
|
|
||||||
// 兼容存量 Blade 调用点,避免一次性重构所有 window.* 入口。
|
// 直接挂载只服务暂未迁移的 Blade 调用点;新代码优先通过模块导入或 ChatRoomTools 复用。
|
||||||
window.closeChatImageLightbox = closeChatImageLightbox;
|
window.closeChatImageLightbox = closeChatImageLightbox;
|
||||||
window.openChatImageLightbox = openChatImageLightbox;
|
window.openChatImageLightbox = openChatImageLightbox;
|
||||||
window.closeFriendPanel = closeFriendPanel;
|
window.closeFriendPanel = closeFriendPanel;
|
||||||
@@ -166,6 +194,14 @@ if (typeof window !== "undefined") {
|
|||||||
window.closeCurrentBaccaratLossCoverEvent = closeCurrentBaccaratLossCoverEvent;
|
window.closeCurrentBaccaratLossCoverEvent = closeCurrentBaccaratLossCoverEvent;
|
||||||
window.openAdminBaccaratLossCoverModal = openAdminBaccaratLossCoverModal;
|
window.openAdminBaccaratLossCoverModal = openAdminBaccaratLossCoverModal;
|
||||||
window.submitBaccaratLossCoverEvent = submitBaccaratLossCoverEvent;
|
window.submitBaccaratLossCoverEvent = submitBaccaratLossCoverEvent;
|
||||||
|
window.bankAction = bankAction;
|
||||||
|
window.bankLoadInfo = bankLoadInfo;
|
||||||
|
window.bankShowMsg = bankShowMsg;
|
||||||
|
window.closeBankModal = closeBankModal;
|
||||||
|
window.fetchBankRanking = fetchBankRanking;
|
||||||
|
window.openBankModal = openBankModal;
|
||||||
|
window.switchBankTab = switchBankTab;
|
||||||
|
window.toggleBankRankSort = toggleBankRankSort;
|
||||||
window.applyFontSize = applyFontSize;
|
window.applyFontSize = applyFontSize;
|
||||||
|
|
||||||
// 页面加载后立即注册事件委托,具体业务逻辑仍由各子模块负责。
|
// 页面加载后立即注册事件委托,具体业务逻辑仍由各子模块负责。
|
||||||
|
|||||||
@@ -1,84 +0,0 @@
|
|||||||
// 金币银行基础按钮事件绑定,替代 toolbar 银行区域内联 onclick。
|
|
||||||
|
|
||||||
let bankControlEventsBound = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 调用银行存量全局函数。
|
|
||||||
*
|
|
||||||
* @param {string} functionName 全局函数名
|
|
||||||
* @param {...unknown} args 参数
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
function callBankGlobal(functionName, ...args) {
|
|
||||||
if (typeof window[functionName] === "function") {
|
|
||||||
window[functionName](...args);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 从分页文本中解析当前页码。
|
|
||||||
*
|
|
||||||
* @returns {number}
|
|
||||||
*/
|
|
||||||
function resolveCurrentRankPage() {
|
|
||||||
const pageInfo = document.getElementById("bank-rank-page-info")?.textContent || "1 / 1";
|
|
||||||
const currentPage = Number.parseInt(pageInfo.split("/")[0]?.trim() || "1", 10);
|
|
||||||
|
|
||||||
return Number.isInteger(currentPage) && currentPage > 0 ? currentPage : 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 绑定银行 tab、存取款、排行榜排序与分页按钮事件。
|
|
||||||
*
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
export function bindBankControls() {
|
|
||||||
if (bankControlEventsBound || typeof document === "undefined") {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
bankControlEventsBound = true;
|
|
||||||
document.addEventListener("click", (event) => {
|
|
||||||
if (!(event.target instanceof Element)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const tabButton = event.target.closest("[data-bank-tab]");
|
|
||||||
if (tabButton) {
|
|
||||||
event.preventDefault();
|
|
||||||
callBankGlobal("switchBankTab", tabButton.getAttribute("data-bank-tab") || "");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (event.target.closest("[data-bank-modal-close]")) {
|
|
||||||
event.preventDefault();
|
|
||||||
callBankGlobal("closeBankModal");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const actionButton = event.target.closest("[data-bank-action]");
|
|
||||||
if (actionButton) {
|
|
||||||
event.preventDefault();
|
|
||||||
// 存取款共用原 bankAction,由 type 决定读取哪个输入框和提交哪个接口。
|
|
||||||
callBankGlobal("bankAction", actionButton.getAttribute("data-bank-action") || "");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (event.target.closest("[data-bank-rank-sort]")) {
|
|
||||||
event.preventDefault();
|
|
||||||
callBankGlobal("toggleBankRankSort");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const pageButton = event.target.closest("[data-bank-rank-page-delta]");
|
|
||||||
if (!pageButton) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
event.preventDefault();
|
|
||||||
|
|
||||||
// 分页状态仍由存量脚本维护,这里从显示文本推导目标页,避免依赖其闭包变量。
|
|
||||||
const delta = Number.parseInt(pageButton.getAttribute("data-bank-rank-page-delta") || "0", 10);
|
|
||||||
callBankGlobal("fetchBankRanking", resolveCurrentRankPage() + delta);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,423 @@
|
|||||||
|
// 金币银行弹窗业务模块,替代 toolbar 中的银行内联脚本。
|
||||||
|
|
||||||
|
import { escapeHtml } from "./html.js";
|
||||||
|
|
||||||
|
let bankRankPage = 1;
|
||||||
|
let bankRankSort = "desc";
|
||||||
|
let bankModalEventsBound = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取 CSRF Token。
|
||||||
|
*
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
function csrf() {
|
||||||
|
return document.querySelector('meta[name="csrf-token"]')?.content ?? "";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置元素显示状态。
|
||||||
|
*
|
||||||
|
* @param {string} id 元素 ID
|
||||||
|
* @param {string} display CSS display 值
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
function setDisplay(id, display) {
|
||||||
|
const element = document.getElementById(id);
|
||||||
|
if (element) {
|
||||||
|
element.style.display = display;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 切换银行弹窗 Tab,并在首次进入排行榜时加载数据。
|
||||||
|
*
|
||||||
|
* @param {string} tabName Tab 名称
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
export function switchBankTab(tabName) {
|
||||||
|
document.getElementById("bank-tabbtn-account")?.classList.remove("active");
|
||||||
|
document.getElementById("bank-tabbtn-ranking")?.classList.remove("active");
|
||||||
|
setDisplay("bank-view-account", "none");
|
||||||
|
setDisplay("bank-view-ranking", "none");
|
||||||
|
|
||||||
|
document.getElementById(`bank-tabbtn-${tabName}`)?.classList.add("active");
|
||||||
|
setDisplay(`bank-view-${tabName}`, "flex");
|
||||||
|
|
||||||
|
const rankingList = document.getElementById("bank-ranking-list");
|
||||||
|
if (tabName === "ranking" && bankRankPage === 1 && rankingList?.innerHTML.includes("加载中")) {
|
||||||
|
void fetchBankRanking(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 打开银行弹窗并刷新账户资金信息。
|
||||||
|
*
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
export function openBankModal() {
|
||||||
|
setDisplay("bank-modal", "flex");
|
||||||
|
switchBankTab("account");
|
||||||
|
void bankLoadInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 关闭银行弹窗。
|
||||||
|
*
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
export function closeBankModal() {
|
||||||
|
setDisplay("bank-modal", "none");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行存款或取款操作。
|
||||||
|
*
|
||||||
|
* @param {"deposit"|"withdraw"|string} type 操作类型
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
export async function bankAction(type) {
|
||||||
|
if (!["deposit", "withdraw"].includes(type)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const inputEl = document.getElementById(`bank-${type}-input`);
|
||||||
|
const btnEl = document.getElementById(`bank-${type}-btn`);
|
||||||
|
if (!(inputEl instanceof HTMLInputElement) || !(btnEl instanceof HTMLButtonElement)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const amount = Number.parseInt(inputEl.value, 10);
|
||||||
|
if (!amount || amount <= 0) {
|
||||||
|
bankShowMsg("请输入有效的金额", false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
btnEl.disabled = true;
|
||||||
|
try {
|
||||||
|
const response = await fetch(`/bank/${type}`, {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"X-CSRF-TOKEN": csrf(),
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
Accept: "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ amount }),
|
||||||
|
});
|
||||||
|
const data = await response.json();
|
||||||
|
bankShowMsg(data.message, data.status === "success");
|
||||||
|
|
||||||
|
if (data.status === "success") {
|
||||||
|
inputEl.value = "";
|
||||||
|
await bankLoadInfo();
|
||||||
|
|
||||||
|
if (document.getElementById("bank-view-ranking")?.style.display !== "none") {
|
||||||
|
await fetchBankRanking(bankRankPage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
bankShowMsg("网络异常,请稍后重试", false);
|
||||||
|
}
|
||||||
|
|
||||||
|
btnEl.disabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前用户资金与最近流水,并同步 chatContext 金币缓存。
|
||||||
|
*
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
export async function bankLoadInfo() {
|
||||||
|
try {
|
||||||
|
const response = await fetch("/bank", {
|
||||||
|
headers: {
|
||||||
|
Accept: "application/json",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const data = await response.json();
|
||||||
|
if (data.status !== "success") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const cashDisplay = document.getElementById("bank-jjb-display");
|
||||||
|
const bankDisplay = document.getElementById("bank-bank-display");
|
||||||
|
if (cashDisplay) {
|
||||||
|
cashDisplay.textContent = Number(data.jjb || 0).toLocaleString();
|
||||||
|
}
|
||||||
|
if (bankDisplay) {
|
||||||
|
bankDisplay.textContent = Number(data.bank_jjb || 0).toLocaleString();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (window.chatContext) {
|
||||||
|
window.chatContext.myGold = data.jjb;
|
||||||
|
window.chatContext.bankGold = data.bank_jjb;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderBankLogs(Array.isArray(data.logs) ? data.logs : []);
|
||||||
|
} catch (error) {
|
||||||
|
const list = document.getElementById("bank-logs-list");
|
||||||
|
if (list) {
|
||||||
|
list.innerHTML = '<div style="text-align:center;color:#ef4444;font-size:12px;padding:30px 0;">加载失败</div>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 渲染最近资金流水。
|
||||||
|
*
|
||||||
|
* @param {Array<Record<string, unknown>>} logs 流水记录
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
function renderBankLogs(logs) {
|
||||||
|
const list = document.getElementById("bank-logs-list");
|
||||||
|
if (!list) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (logs.length === 0) {
|
||||||
|
list.innerHTML = '<div style="text-align:center;color:#9db3d4;font-size:12px;padding:30px 0;">暂无记录</div>';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
list.innerHTML = logs.map((log) => {
|
||||||
|
const isDeposit = log.type === "deposit";
|
||||||
|
const className = isDeposit ? "bank-log-deposit" : "bank-log-withdraw";
|
||||||
|
const sign = isDeposit ? "+ 存入" : "- 取出";
|
||||||
|
const createdAt = String(log.created_at || "").replace("T", " ").substring(0, 16);
|
||||||
|
|
||||||
|
return `<div class="bank-log-item">
|
||||||
|
<span class="${className}">${sign} ${Number(log.amount || 0).toLocaleString()} 金币</span>
|
||||||
|
<span style="color:#64748b;">余额: <strong style="color:#334155">${Number(log.balance_after || 0).toLocaleString()}</strong></span>
|
||||||
|
<span style="color:#94a3b8;">${escapeHtml(createdAt)}</span>
|
||||||
|
</div>`;
|
||||||
|
}).join("");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 显示银行操作结果提示。
|
||||||
|
*
|
||||||
|
* @param {string} message 提示内容
|
||||||
|
* @param {boolean} success 是否成功
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
export function bankShowMsg(message, success) {
|
||||||
|
const element = document.getElementById("bank-op-msg");
|
||||||
|
if (!element) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
element.textContent = message;
|
||||||
|
element.style.background = success ? "#f0fdf4" : "#fef2f2";
|
||||||
|
element.style.border = success ? "1px solid #bbf7d0" : "1px solid #fecaca";
|
||||||
|
element.style.color = success ? "#16a34a" : "#ef4444";
|
||||||
|
element.style.display = "block";
|
||||||
|
clearTimeout(element._t);
|
||||||
|
element._t = setTimeout(() => {
|
||||||
|
element.style.display = "none";
|
||||||
|
}, 3000);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 切换排行榜排序并回到第一页。
|
||||||
|
*
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
export function toggleBankRankSort() {
|
||||||
|
bankRankSort = bankRankSort === "desc" ? "asc" : "desc";
|
||||||
|
|
||||||
|
const button = document.getElementById("bank-rank-sort-btn");
|
||||||
|
if (button) {
|
||||||
|
button.textContent = bankRankSort === "desc" ? "降序 ↓" : "升序 ↑";
|
||||||
|
}
|
||||||
|
|
||||||
|
void fetchBankRanking(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 拉取并渲染存款排行榜。
|
||||||
|
*
|
||||||
|
* @param {number} page 页码
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
export async function fetchBankRanking(page) {
|
||||||
|
if (page < 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const listEl = document.getElementById("bank-ranking-list");
|
||||||
|
if (!listEl) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
listEl.innerHTML = '<div style="text-align:center; color:#9db3d4; font-size:12px; padding:30px 0;">加载中...</div>';
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(`/bank/ranking?page=${page}&sort=${bankRankSort}`, {
|
||||||
|
headers: {
|
||||||
|
Accept: "application/json",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const data = await response.json();
|
||||||
|
|
||||||
|
if (data.status !== "success") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bankRankPage = data.pagination.current_page;
|
||||||
|
const pageInfo = document.getElementById("bank-rank-page-info");
|
||||||
|
if (pageInfo) {
|
||||||
|
pageInfo.textContent = `${bankRankPage} / ${data.pagination.last_page}`;
|
||||||
|
}
|
||||||
|
syncRankingButtons(data.pagination.last_page);
|
||||||
|
|
||||||
|
if (!Array.isArray(data.ranking) || data.ranking.length === 0) {
|
||||||
|
listEl.innerHTML = '<div style="text-align:center; color:#9db3d4; font-size:12px; padding:30px 0;">暂无数据</div>';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
listEl.innerHTML = data.ranking.map((user, index) => renderRankingRow(user, index)).join("");
|
||||||
|
} catch (error) {
|
||||||
|
listEl.innerHTML = '<div style="text-align:center; color:#ef4444; font-size:12px; padding:30px 0;">拉取失败</div>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 同步排行榜分页按钮状态。
|
||||||
|
*
|
||||||
|
* @param {number} lastPage 最后一页页码
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
function syncRankingButtons(lastPage) {
|
||||||
|
const prevButton = document.getElementById("bank-rank-prev");
|
||||||
|
const nextButton = document.getElementById("bank-rank-next");
|
||||||
|
if (!(prevButton instanceof HTMLButtonElement) || !(nextButton instanceof HTMLButtonElement)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
prevButton.disabled = bankRankPage <= 1;
|
||||||
|
prevButton.style.opacity = bankRankPage <= 1 ? "0.5" : "1";
|
||||||
|
nextButton.disabled = bankRankPage >= lastPage;
|
||||||
|
nextButton.style.opacity = bankRankPage >= lastPage ? "0.5" : "1";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 渲染排行榜单行;前三名样式只在降序第一页生效。
|
||||||
|
*
|
||||||
|
* @param {Record<string, unknown>} user 用户排行数据
|
||||||
|
* @param {number} index 当前页索引
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
function renderRankingRow(user, index) {
|
||||||
|
const absoluteRank = (bankRankPage - 1) * 20 + index + 1;
|
||||||
|
let rankClass = "";
|
||||||
|
if (bankRankSort === "desc" && bankRankPage === 1) {
|
||||||
|
if (absoluteRank === 1) {
|
||||||
|
rankClass = "bank-rank-top1";
|
||||||
|
} else if (absoluteRank === 2) {
|
||||||
|
rankClass = "bank-rank-top2";
|
||||||
|
} else if (absoluteRank === 3) {
|
||||||
|
rankClass = "bank-rank-top3";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const avatarUrl = escapeHtml(resolveSafeBankAvatarUrl(user.headfaceUrl));
|
||||||
|
const username = escapeHtml(user.username || "");
|
||||||
|
const sexSymbol = user.sex === "女" ? "♀" : "♂";
|
||||||
|
|
||||||
|
return `<div class="bank-rank-item">
|
||||||
|
<div class="bank-rank-num ${rankClass}">${absoluteRank}</div>
|
||||||
|
<img src="${avatarUrl}" onerror="this.src='/images/headface/1.gif'" style="width:32px; height:32px; border-radius:50%; object-fit:cover; border:1px solid #d0e4f5;">
|
||||||
|
<div>
|
||||||
|
<div style="font-weight:bold; color:#1e3a8a;">${username} <span style="font-size:12px;">${sexSymbol}</span></div>
|
||||||
|
</div>
|
||||||
|
<div class="bank-rank-val">
|
||||||
|
🏦 ${Number(user.bank_jjb || 0).toLocaleString()}
|
||||||
|
</div>
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 规整排行榜头像地址,避免任意 URL 直接进入 img src。
|
||||||
|
*
|
||||||
|
* @param {unknown} value 头像地址
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
function resolveSafeBankAvatarUrl(value) {
|
||||||
|
const url = String(value || "");
|
||||||
|
|
||||||
|
if (url.startsWith("/images/headface/") || url.startsWith("/storage/")) {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
return "/images/headface/1.gif";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从分页文本中解析当前页码。
|
||||||
|
*
|
||||||
|
* @returns {number}
|
||||||
|
*/
|
||||||
|
function resolveCurrentRankPage() {
|
||||||
|
const pageInfo = document.getElementById("bank-rank-page-info")?.textContent || "1 / 1";
|
||||||
|
const currentPage = Number.parseInt(pageInfo.split("/")[0]?.trim() || "1", 10);
|
||||||
|
|
||||||
|
return Number.isInteger(currentPage) && currentPage > 0 ? currentPage : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 绑定银行弹窗按钮和遮罩关闭事件。
|
||||||
|
*
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
export function bindBankControls() {
|
||||||
|
if (bankModalEventsBound || typeof document === "undefined") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bankModalEventsBound = true;
|
||||||
|
document.addEventListener("click", (event) => {
|
||||||
|
if (!(event.target instanceof Element)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const tabButton = event.target.closest("[data-bank-tab]");
|
||||||
|
if (tabButton) {
|
||||||
|
event.preventDefault();
|
||||||
|
switchBankTab(tabButton.getAttribute("data-bank-tab") || "");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.target.closest("[data-bank-modal-close]")) {
|
||||||
|
event.preventDefault();
|
||||||
|
closeBankModal();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const actionButton = event.target.closest("[data-bank-action]");
|
||||||
|
if (actionButton) {
|
||||||
|
event.preventDefault();
|
||||||
|
void bankAction(actionButton.getAttribute("data-bank-action") || "");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.target.closest("[data-bank-rank-sort]")) {
|
||||||
|
event.preventDefault();
|
||||||
|
toggleBankRankSort();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const pageButton = event.target.closest("[data-bank-rank-page-delta]");
|
||||||
|
if (pageButton) {
|
||||||
|
event.preventDefault();
|
||||||
|
const delta = Number.parseInt(pageButton.getAttribute("data-bank-rank-page-delta") || "0", 10);
|
||||||
|
void fetchBankRanking(resolveCurrentRankPage() + delta);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const modal = event.target.closest("#bank-modal");
|
||||||
|
// 点击遮罩关闭,只响应最外层背景,避免内容区误关。
|
||||||
|
if (modal && event.target === modal) {
|
||||||
|
closeBankModal();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -2518,183 +2518,3 @@ async function generateWechatBindCode() {
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
|
||||||
let bankRankPage = 1;
|
|
||||||
let bankRankSort = 'desc';
|
|
||||||
|
|
||||||
/** 切换 Tab */
|
|
||||||
function switchBankTab(tabName) {
|
|
||||||
document.getElementById('bank-tabbtn-account').classList.remove('active');
|
|
||||||
document.getElementById('bank-tabbtn-ranking').classList.remove('active');
|
|
||||||
document.getElementById('bank-view-account').style.display = 'none';
|
|
||||||
document.getElementById('bank-view-ranking').style.display = 'none';
|
|
||||||
|
|
||||||
document.getElementById('bank-tabbtn-' + tabName).classList.add('active');
|
|
||||||
document.getElementById('bank-view-' + tabName).style.display = 'flex';
|
|
||||||
|
|
||||||
if (tabName === 'ranking' && bankRankPage === 1 && document.getElementById('bank-ranking-list').innerHTML.includes('加载中')) {
|
|
||||||
fetchBankRanking(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 打开弹窗 */
|
|
||||||
function openBankModal() {
|
|
||||||
document.getElementById('bank-modal').style.display = 'flex';
|
|
||||||
switchBankTab('account');
|
|
||||||
bankLoadInfo();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 关闭弹窗 */
|
|
||||||
function closeBankModal() {
|
|
||||||
document.getElementById('bank-modal').style.display = 'none';
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 存取款合并 */
|
|
||||||
async function bankAction(type) {
|
|
||||||
const inputEl = document.getElementById(`bank-${type}-input`);
|
|
||||||
const btnEl = document.getElementById(`bank-${type}-btn`);
|
|
||||||
const amount = parseInt(inputEl.value, 10);
|
|
||||||
if (!amount || amount <= 0) {
|
|
||||||
bankShowMsg('请输入有效的金额', false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
btnEl.disabled = true;
|
|
||||||
try {
|
|
||||||
const res = await fetch(`/bank/${type}`, {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content,
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
'Accept': 'application/json',
|
|
||||||
},
|
|
||||||
body: JSON.stringify({ amount }),
|
|
||||||
});
|
|
||||||
const data = await res.json();
|
|
||||||
bankShowMsg(data.message, data.status === 'success');
|
|
||||||
if (data.status === 'success') {
|
|
||||||
inputEl.value = '';
|
|
||||||
bankLoadInfo();
|
|
||||||
if (document.getElementById('bank-view-ranking').style.display !== 'none') {
|
|
||||||
fetchBankRanking(bankRankPage);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
bankShowMsg('网络异常,请稍后重试', false);
|
|
||||||
}
|
|
||||||
btnEl.disabled = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 获取资金与流水 */
|
|
||||||
async function bankLoadInfo() {
|
|
||||||
try {
|
|
||||||
const res = await fetch('/bank', { headers: { 'Accept': 'application/json' } });
|
|
||||||
const data = await res.json();
|
|
||||||
if (data.status !== 'success') return;
|
|
||||||
|
|
||||||
document.getElementById('bank-jjb-display').textContent = data.jjb.toLocaleString();
|
|
||||||
document.getElementById('bank-bank-display').textContent = data.bank_jjb.toLocaleString();
|
|
||||||
|
|
||||||
if (window.chatContext) {
|
|
||||||
window.chatContext.myGold = data.jjb;
|
|
||||||
window.chatContext.bankGold = data.bank_jjb;
|
|
||||||
}
|
|
||||||
|
|
||||||
const list = document.getElementById('bank-logs-list');
|
|
||||||
if (data.logs.length === 0) {
|
|
||||||
list.innerHTML = '<div style="text-align:center;color:#9db3d4;font-size:12px;padding:30px 0;">暂无记录</div>';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
list.innerHTML = data.logs.map(log => {
|
|
||||||
const isDeposit = log.type === 'deposit';
|
|
||||||
const cls = isDeposit ? 'bank-log-deposit' : 'bank-log-withdraw';
|
|
||||||
const sign = isDeposit ? '+ 存入' : '- 取出';
|
|
||||||
const dt = log.created_at.replace('T', ' ').substring(0, 16);
|
|
||||||
return `<div class="bank-log-item">
|
|
||||||
<span class="${cls}">${sign} ${log.amount.toLocaleString()} 金币</span>
|
|
||||||
<span style="color:#64748b;">余额: <strong style="color:#334155">${log.balance_after.toLocaleString()}</strong></span>
|
|
||||||
<span style="color:#94a3b8;">${dt}</span>
|
|
||||||
</div>`;
|
|
||||||
}).join('');
|
|
||||||
} catch (e) {
|
|
||||||
document.getElementById('bank-logs-list').innerHTML = '<div style="text-align:center;color:#ef4444;font-size:12px;padding:30px 0;">加载失败</div>';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function bankShowMsg(msg, success) {
|
|
||||||
const el = document.getElementById('bank-op-msg');
|
|
||||||
el.textContent = msg;
|
|
||||||
el.style.background = success ? '#f0fdf4' : '#fef2f2';
|
|
||||||
el.style.border = success ? '1px solid #bbf7d0' : '1px solid #fecaca';
|
|
||||||
el.style.color = success ? '#16a34a' : '#ef4444';
|
|
||||||
el.style.display = 'block';
|
|
||||||
clearTimeout(el._t);
|
|
||||||
el._t = setTimeout(() => { el.style.display = 'none'; }, 3000);
|
|
||||||
}
|
|
||||||
|
|
||||||
function toggleBankRankSort() {
|
|
||||||
bankRankSort = bankRankSort === 'desc' ? 'asc' : 'desc';
|
|
||||||
document.getElementById('bank-rank-sort-btn').textContent = bankRankSort === 'desc' ? '降序 ↓' : '升序 ↑';
|
|
||||||
fetchBankRanking(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function fetchBankRanking(page) {
|
|
||||||
if (page < 1) return;
|
|
||||||
const listEl = document.getElementById('bank-ranking-list');
|
|
||||||
listEl.innerHTML = '<div style="text-align:center; color:#9db3d4; font-size:12px; padding:30px 0;">加载中...</div>';
|
|
||||||
|
|
||||||
try {
|
|
||||||
const res = await fetch(`/bank/ranking?page=${page}&sort=${bankRankSort}`, { headers: {'Accept': 'application/json'} });
|
|
||||||
const data = await res.json();
|
|
||||||
|
|
||||||
if (data.status === 'success') {
|
|
||||||
bankRankPage = data.pagination.current_page;
|
|
||||||
document.getElementById('bank-rank-page-info').textContent = `${bankRankPage} / ${data.pagination.last_page}`;
|
|
||||||
|
|
||||||
const btnPrev = document.getElementById('bank-rank-prev');
|
|
||||||
const btnNext = document.getElementById('bank-rank-next');
|
|
||||||
|
|
||||||
btnPrev.disabled = bankRankPage <= 1;
|
|
||||||
btnPrev.style.opacity = bankRankPage <= 1 ? 0.5 : 1;
|
|
||||||
btnNext.disabled = bankRankPage >= data.pagination.last_page;
|
|
||||||
btnNext.style.opacity = bankRankPage >= data.pagination.last_page ? 0.5 : 1;
|
|
||||||
|
|
||||||
if (data.ranking.length === 0) {
|
|
||||||
listEl.innerHTML = '<div style="text-align:center; color:#9db3d4; font-size:12px; padding:30px 0;">暂无数据</div>';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let html = '';
|
|
||||||
data.ranking.forEach((u, index) => {
|
|
||||||
const absNum = (bankRankPage - 1) * 20 + index + 1;
|
|
||||||
let rankCls = '';
|
|
||||||
if (bankRankSort === 'desc' && bankRankPage === 1) {
|
|
||||||
if (absNum === 1) rankCls = 'bank-rank-top1';
|
|
||||||
else if (absNum === 2) rankCls = 'bank-rank-top2';
|
|
||||||
else if (absNum === 3) rankCls = 'bank-rank-top3';
|
|
||||||
}
|
|
||||||
html += `
|
|
||||||
<div class="bank-rank-item">
|
|
||||||
<div class="bank-rank-num ${rankCls}">${absNum}</div>
|
|
||||||
<img src="${u.headfaceUrl || ''}" onerror="this.src='/images/headface/1.gif'" style="width:32px; height:32px; border-radius:50%; object-fit:cover; border:1px solid #d0e4f5;">
|
|
||||||
<div>
|
|
||||||
<div style="font-weight:bold; color:#1e3a8a;">${u.username} <span style="font-size:12px;">${u.sex === '女' ? '♀' : '♂'}</span></div>
|
|
||||||
</div>
|
|
||||||
<div class="bank-rank-val">
|
|
||||||
🏦 ${Number(u.bank_jjb).toLocaleString()}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
});
|
|
||||||
listEl.innerHTML = html;
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
listEl.innerHTML = '<div style="text-align:center; color:#ef4444; font-size:12px; padding:30px 0;">拉取失败</div>';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
document.getElementById('bank-modal').addEventListener('click', function(e) {
|
|
||||||
if (e.target === this) closeBankModal();
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|||||||
Reference in New Issue
Block a user