Files
web_huangguo_web/app/static/js/pages/deposit.js
T
fish03andClaude Opus 5.5 bef9f8b4ed Initial commit
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-23 14:43:16 +07:00

482 lines
16 KiB
JavaScript

/**
* 充值页:套餐 / 支付方式互斥选择,按套餐 payMethods 刷新;提交走 VIP/金币下单。
* VIP: POST /api/vip/pay 金币: POST /api/coin/buy
* 支付方式严格使用接口返回的 payMethods,不造假默认渠道。
*/
(function () {
var submitting = false;
var confirmMask = document.querySelector("[data-deposit-confirm]");
var confirmMethods = document.querySelector("[data-deposit-confirm-methods]");
var confirmSubmit = document.querySelector("[data-deposit-confirm-submit]");
var bodyOverflowBeforeConfirm = "";
function toast(message, type) {
if (window.AuthAPI && typeof AuthAPI.toast === "function") {
AuthAPI.toast(message, type || "info");
return;
}
if (typeof showToast === "function") showToast(message, type || "info");
}
function bindExclusive(root, selector, activeClass) {
var scope = root || document;
var nodes = scope.querySelectorAll(selector);
if (!nodes.length) return;
nodes.forEach(function (node) {
node.addEventListener("click", function () {
nodes.forEach(function (item) {
item.classList.remove(activeClass);
});
node.classList.add(activeClass);
syncPayTypeAcross(node.getAttribute("data-pay-type") || "");
});
});
}
function syncPayTypeAcross(payType) {
if (!payType) return;
document.querySelectorAll("[data-pay-type]").forEach(function (el) {
var match = String(el.getAttribute("data-pay-type") || "") === String(payType);
if (el.classList.contains("deposit-pay-btn")) {
el.classList.toggle("deposit-pay-btn--active", match);
}
if (el.classList.contains("deposit-mobile-pay")) {
el.classList.toggle("deposit-mobile-pay--active", match);
}
if (el.classList.contains("deposit-confirm-method")) {
el.classList.toggle("is-active", match);
}
});
}
function parsePayMethods(raw) {
if (!raw) return [];
try {
var list = JSON.parse(raw);
if (!Array.isArray(list)) return [];
return list
.map(function (item) {
return {
type: String((item && item.type) || "").trim(),
typeName: String((item && (item.typeName || item.type)) || "").trim(),
};
})
.filter(function (item) {
return item.type || item.typeName;
});
} catch (e) {
return [];
}
}
function currentPayType() {
var active =
document.querySelector(".deposit-confirm-method.is-active[data-pay-type]") ||
document.querySelector(".deposit-pay-btn--active[data-pay-type]") ||
document.querySelector(".deposit-mobile-pay--active[data-pay-type]");
return active ? String(active.getAttribute("data-pay-type") || "") : "";
}
function currentPackage() {
return (
document.querySelector(".deposit-mobile-pkg--active[data-package-id]") ||
document.querySelector(".deposit-package--active[data-package-id]")
);
}
function renderPayButtons(container, className, activeClass, methods, preferType) {
if (!container) return;
var list = Array.isArray(methods) ? methods : [];
if (!list.length) {
container.innerHTML = '<div class="deposit-package-empty">暂无可用支付方式</div>';
return;
}
var prefer = preferType || "";
var activeType = prefer;
if (!list.some(function (item) { return item.type === prefer; })) {
activeType = list[0] ? list[0].type : "";
}
container.innerHTML = list
.map(function (item) {
var isActive = item.type === activeType;
return (
'<button class="' +
className +
(isActive ? " " + activeClass : "") +
'" type="button" data-pay-type="' +
escapeAttr(item.type) +
'">' +
escapeHtml(item.typeName || item.type) +
"</button>"
);
})
.join("");
bindExclusive(container, "." + className.split(" ").join("."), activeClass);
}
function escapeHtml(text) {
return String(text || "")
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;");
}
function escapeAttr(text) {
return escapeHtml(text).replace(/'/g, "&#39;");
}
function payMethodVisual(item) {
var value = String((item && (item.typeName || item.type)) || "").toLowerCase();
if (/支付宝|alipay/.test(value)) {
return { kind: "alipay", icon: "/static/images/figma-dialogs/alipay.svg" };
}
if (/微信|wechat|weixin|wxpay/.test(value)) {
return { kind: "wechat", icon: "/static/images/figma-dialogs/wechat.svg" };
}
if (/usdt|tether/.test(value)) {
return { kind: "usdt", icon: "/static/images/figma-dialogs/usdt.svg" };
}
return { kind: "other", icon: "" };
}
function renderConfirmMethods(methods, preferType) {
if (!confirmMethods) return "";
var list = Array.isArray(methods) ? methods : [];
var activeType = preferType || (list[0] && list[0].type) || "";
if (!list.some(function (item) { return item.type === activeType; })) {
activeType = (list[0] && list[0].type) || "";
}
confirmMethods.innerHTML = list.map(function (item) {
var visual = payMethodVisual(item);
var icon = visual.icon
? '<img src="' + visual.icon + '" alt="" />'
: '<span aria-hidden="true">¥</span>';
return '<button class="deposit-confirm-method' +
(item.type === activeType ? ' is-active' : '') +
'" type="button" data-pay-type="' + escapeAttr(item.type) + '">' +
'<span class="deposit-confirm-method-copy"><span class="deposit-confirm-method-icon deposit-confirm-method-icon--' + visual.kind + '">' + icon + '</span>' +
'<span class="deposit-confirm-method-name">' + escapeHtml(item.typeName || item.type) + '</span></span>' +
'<span class="deposit-confirm-method-check" aria-hidden="true">✓</span></button>';
}).join("");
confirmMethods.querySelectorAll(".deposit-confirm-method").forEach(function (button) {
button.addEventListener("click", function () {
syncPayTypeAcross(button.getAttribute("data-pay-type") || "");
});
});
syncPayTypeAcross(activeType);
return activeType;
}
function hasPayMethods(el) {
var target = el || currentPackage();
if (!target) return false;
return parsePayMethods(target.getAttribute("data-pay-methods")).length > 0;
}
function syncPayMethods(el) {
var methods = parsePayMethods(el.getAttribute("data-pay-methods"));
var prefer = currentPayType();
renderPayButtons(
document.querySelector("[data-deposit-pay-pc]"),
"deposit-pay-btn",
"deposit-pay-btn--active",
methods,
prefer
);
renderPayButtons(
document.querySelector("[data-deposit-pay-mobile]"),
"deposit-mobile-pay",
"deposit-mobile-pay--active",
methods,
prefer
);
}
function syncPackageActive(kind, packageId) {
document.querySelectorAll(".deposit-package, .deposit-mobile-pkg").forEach(function (el) {
var match =
String(el.getAttribute("data-package-id") || "") === String(packageId) &&
String(el.getAttribute("data-package-kind") || "") === String(kind);
el.classList.toggle("deposit-package--active", match && el.classList.contains("deposit-package"));
el.classList.toggle(
"deposit-mobile-pkg--active",
match && el.classList.contains("deposit-mobile-pkg")
);
});
}
function updateSubmitButtons(price, loading) {
var canPay = hasPayMethods(currentPackage());
var disabled = !!loading || !canPay;
var pkg = currentPackage();
var packageName = pkg ? String(pkg.getAttribute("data-package-name") || "").trim() : "";
var text;
if (loading) {
text = "支付中...";
} else if (!canPay) {
text = "暂无支付方式";
} else {
text = "立即支付 ¥" + (price || "0");
}
document.querySelectorAll("[data-deposit-submit]").forEach(function (btn) {
btn.disabled = disabled;
btn.classList.toggle("is-loading", !!loading);
btn.classList.toggle("is-disabled", !canPay && !loading);
if (btn.classList.contains("deposit-pc-submit")) {
btn.textContent = loading ? "支付中..." : canPay ? "立即支付 ¥" + (price || "0") : "暂无支付方式";
} else {
btn.textContent = loading ? "支付中..." : canPay ? "支付 ¥" + (price || "0") + " 充值金币" : text;
}
});
document.querySelectorAll("[data-deposit-selected-note]").forEach(function (node) {
node.textContent = packageName
? "已选择 " + packageName + " · 点击后选择支付方式"
: "请选择金币套餐";
});
}
function onPackageClick(el) {
var kind = el.getAttribute("data-package-kind") || "";
var id = el.getAttribute("data-package-id") || "";
var price = el.getAttribute("data-price") || "0";
syncPackageActive(kind, id);
syncPayMethods(el);
updateSubmitButtons(price, false);
}
function openPayUrl(data) {
var url = data && data.payUrl ? String(data.payUrl).trim() : "";
if (!url) {
toast("未获取到支付链接", "error");
return;
}
var mode = String((data && data.payUrlMode) || "").toLowerCase();
var openNew =
!!(data && data.isOpenNewBrowser) ||
mode === "new" ||
mode === "blank" ||
mode === "external";
if (openNew) {
var win = window.open(url, "_blank");
if (!win) {
window.location.href = url;
}
return;
}
window.location.href = url;
}
function ensureLoggedIn() {
if (window.CookieUtil && CookieUtil.isLoggedIn()) return true;
toast("请先登录后再支付", "error");
var loginURL = "/login/return/deposit/";
if (typeof window.openLoginDialog === "function") {
var opened = window.openLoginDialog();
if (opened && typeof opened.then === "function") {
opened.catch(function () {
window.location.href = loginURL;
});
}
} else {
window.location.href = loginURL;
}
return false;
}
function closePayConfirm() {
if (!confirmMask) return;
confirmMask.classList.remove("is-open");
confirmMask.setAttribute("aria-hidden", "true");
document.body.style.overflow = bodyOverflowBeforeConfirm;
}
function openPayConfirm() {
if (submitting || !confirmMask) return;
if (!ensureLoggedIn()) return;
var pkg = currentPackage();
if (!pkg) {
toast("请选择套餐", "error");
return;
}
var methods = parsePayMethods(pkg.getAttribute("data-pay-methods"));
if (!methods.length) {
toast("当前套餐暂无可用支付方式", "error");
return;
}
var packageName = String(pkg.getAttribute("data-package-name") || "金币套餐").trim();
var price = String(pkg.getAttribute("data-price") || "0").trim();
var nameEl = confirmMask.querySelector("[data-deposit-confirm-name]");
var priceEl = confirmMask.querySelector("[data-deposit-confirm-price]");
if (nameEl) nameEl.textContent = packageName;
if (priceEl) priceEl.textContent = price;
renderConfirmMethods(methods, currentPayType());
bodyOverflowBeforeConfirm = document.body.style.overflow;
document.body.style.overflow = "hidden";
confirmMask.classList.add("is-open");
confirmMask.setAttribute("aria-hidden", "false");
}
function submitPay() {
if (submitting) return;
if (!window.HttpRequest) {
toast("请求组件未加载", "error");
return;
}
if (!ensureLoggedIn()) return;
var pkg = currentPackage();
if (!pkg) {
toast("请选择套餐", "error");
return;
}
var packageId = Number(pkg.getAttribute("data-package-id") || 0);
var kind = String(pkg.getAttribute("data-package-kind") || "");
var price = pkg.getAttribute("data-price") || "0";
var payMode = currentPayType();
if (!packageId || (kind !== "vip" && kind !== "coin")) {
toast("套餐无效,请重新选择", "error");
return;
}
if (!payMode) {
toast("请选择支付方式", "error");
return;
}
if (!hasPayMethods(pkg)) {
toast("当前套餐暂无可用支付方式", "error");
return;
}
var api = kind === "coin" ? "/api/coin/buy" : "/api/vip/pay";
submitting = true;
updateSubmitButtons(price, true);
if (confirmSubmit) {
confirmSubmit.disabled = true;
confirmSubmit.textContent = "支付中...";
}
HttpRequest.post(api, { packageId: packageId, payMode: payMode })
.then(function (res) {
if (!res || res.code !== 200 || !res.data) {
var err = new Error((res && (res.tip || res.msg)) || "发起支付失败");
err.code = res && res.code;
throw err;
}
openPayUrl(res.data);
})
.catch(function (err) {
if (err && err.code === 401) {
toast("请先登录后再支付", "error");
if (typeof window.openLoginDialog === "function") {
var opened = window.openLoginDialog();
if (opened && typeof opened.then === "function") {
opened.catch(function () {
window.location.href = "/login/return/deposit/";
});
}
}
return;
}
toast((err && err.message) || "发起支付失败", "error");
})
.finally(function () {
submitting = false;
updateSubmitButtons(price, false);
if (confirmSubmit) {
confirmSubmit.disabled = false;
confirmSubmit.textContent = "去支付";
}
});
}
bindExclusive(document, ".deposit-pay-btn", "deposit-pay-btn--active");
bindExclusive(document, ".deposit-mobile-pay", "deposit-mobile-pay--active");
document.querySelectorAll(".deposit-package[data-package-id]").forEach(function (node) {
node.addEventListener("click", function () {
onPackageClick(node);
});
});
document.querySelectorAll(".deposit-mobile-pkg[data-package-id]").forEach(function (node) {
node.addEventListener("click", function () {
onPackageClick(node);
});
});
document.querySelectorAll("[data-deposit-submit]").forEach(function (btn) {
btn.addEventListener("click", openPayConfirm);
});
if (confirmSubmit) confirmSubmit.addEventListener("click", submitPay);
document.querySelectorAll("[data-deposit-confirm-close]").forEach(function (button) {
button.addEventListener("click", closePayConfirm);
});
if (confirmMask) {
confirmMask.addEventListener("click", function (event) {
if (event.target === confirmMask) closePayConfirm();
});
}
document.addEventListener("keydown", function (event) {
if (event.key === "Escape" && confirmMask && confirmMask.classList.contains("is-open")) {
closePayConfirm();
}
});
var active =
document.querySelector(".deposit-mobile-pkg--active[data-price]") ||
document.querySelector(".deposit-package--active[data-price]");
if (active) {
syncPayMethods(active);
updateSubmitButtons(active.getAttribute("data-price") || "0", false);
} else {
updateSubmitButtons("0", false);
}
// /deposit/coin 时滚动到金币充值区域(套餐默认选中由 SSR 完成)
(function scrollPreferCoin() {
if (!/\/deposit\/coin\/?$/i.test(window.location.pathname || "")) return;
var isMobile = window.matchMedia("(max-width: 767px)").matches;
var target = isMobile
? document.getElementById("depositCoinMobileTitle")
: document.getElementById("depositCoinGroup");
if (!target) return;
setTimeout(function () {
target.scrollIntoView({ behavior: "smooth", block: "start" });
}, 80);
})();
function formatRecharge(value) {
var n = Number(value);
if (!isFinite(n) || n < 0) n = 0;
return String(Math.floor(n)).replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}
function syncDepositRecharge(value) {
var text = formatRecharge(value);
document.querySelectorAll("[data-deposit-recharge]").forEach(function (el) {
el.textContent = text;
});
}
function refreshDepositWallet() {
if (!window.CookieUtil || !CookieUtil.isLoggedIn() || !window.HttpRequest) {
return;
}
var session = CookieUtil.getAuthSession();
if (session && session.recharge != null && session.recharge !== "") {
syncDepositRecharge(session.recharge);
}
HttpRequest.get("/api/auth/getInfo")
.then(function (res) {
if (!res || res.code !== 200 || !res.data) return;
if (CookieUtil.setAuthSession) CookieUtil.setAuthSession(res.data);
syncDepositRecharge(res.data.recharge);
})
.catch(function () {});
}
refreshDepositWallet();
})();