- detail-tab 累计佣金 row now shows 累计 + 已提现 (was 累计/可提现/已提现) - rebuild poster canvas to match reference layout (768×1168, top banner, date overlay, blue title bar, bottom bordered box with QR + link) - copy-link feedback moves from inline text to a toast (notyf), matching the reference; drop the now-unused data-affiliate-feedback element
1304 lines
52 KiB
JavaScript
1304 lines
52 KiB
JavaScript
/**
|
||
* AKSRT Credits Frontend
|
||
*
|
||
* 功能:
|
||
* - 签到 panel:跳转至全局签到按钮
|
||
* - revenue panel:拉取 /revenue/me,复制推广链接
|
||
*
|
||
* 仅在 page-profile 页面 + 启用积分时加载。
|
||
*/
|
||
(function () {
|
||
'use strict';
|
||
|
||
var aksrt = window.aksrtData;
|
||
if (!aksrt) {
|
||
return;
|
||
}
|
||
var activePoll = null;
|
||
|
||
function operationKey(owner) {
|
||
if (!owner.dataset.aksrtOperationKey) {
|
||
owner.dataset.aksrtOperationKey = window.AksrtRequest.createIdempotencyKey();
|
||
}
|
||
return owner.dataset.aksrtOperationKey;
|
||
}
|
||
|
||
function resetOperationKey(owner) {
|
||
delete owner.dataset.aksrtOperationKey;
|
||
}
|
||
|
||
function resetOperationKeyOnChange(owner) {
|
||
owner.addEventListener('input', function () { resetOperationKey(owner); });
|
||
owner.addEventListener('change', function () { resetOperationKey(owner); });
|
||
}
|
||
|
||
function balanceMinor(value) {
|
||
var match = /^(0|[1-9]\d*)(?:\.(\d{1,2}))?$/.exec(String(value || '').trim());
|
||
if (!match) return null;
|
||
return (BigInt(match[1]) * 100n) + BigInt((match[2] || '').padEnd(2, '0') || '0');
|
||
}
|
||
|
||
function api(path, options) {
|
||
return window.AksrtRequest.request(path, options || {}).then(function (response) {
|
||
return {
|
||
ok: response.ok,
|
||
status: response.status,
|
||
json: response.json || {},
|
||
data: response.data,
|
||
};
|
||
}).catch(function (error) {
|
||
var response = error && error.response ? error.response : {};
|
||
return {
|
||
ok: false,
|
||
status: error && error.status ? error.status : (response.status || 0),
|
||
json: error && error.json ? error.json : (response.json || { message: error && error.message }),
|
||
};
|
||
});
|
||
}
|
||
|
||
function applyAssetBalances(data) {
|
||
data = data || {};
|
||
var wallet = data.wallet_balance !== undefined ? data.wallet_balance : data.wallet;
|
||
var points = data.credit_balance !== undefined ? data.credit_balance : data.points_balance;
|
||
if (wallet !== undefined && wallet !== null && wallet !== '') {
|
||
var walletNumber = Number(wallet);
|
||
var walletText = Number.isFinite(walletNumber) ? walletNumber.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 }) : String(wallet);
|
||
document.querySelectorAll('[data-aksrt-wallet-value]').forEach(function (element) {
|
||
element.textContent = walletText;
|
||
element.title = walletText;
|
||
});
|
||
}
|
||
if (points !== undefined && points !== null && points !== '') {
|
||
var pointsNumber = Number(points);
|
||
var pointsText = Number.isFinite(pointsNumber) ? pointsNumber.toLocaleString() : String(points);
|
||
document.querySelectorAll('[data-aksrt-points-value]').forEach(function (element) {
|
||
element.textContent = pointsText;
|
||
element.title = pointsText;
|
||
});
|
||
}
|
||
}
|
||
|
||
function refreshAssetBalances(snapshot) {
|
||
if (snapshot && (snapshot.wallet_balance !== undefined || snapshot.wallet !== undefined)) {
|
||
applyAssetBalances(snapshot);
|
||
}
|
||
return api('/pay/assets').then(function (response) {
|
||
if (!response.ok) return null;
|
||
var data = response.data || (response.json && response.json.data) || response.json || {};
|
||
applyAssetBalances(data);
|
||
return data;
|
||
});
|
||
}
|
||
|
||
// Task-list "去签到" → scroll + click the real asset-card sign-in button.
|
||
function initSigninGoto() {
|
||
document.querySelectorAll('[data-aksrt-signin-goto]').forEach(function (el) {
|
||
if (el.dataset.aksrtSigninGotoInitialized === '1') return;
|
||
el.dataset.aksrtSigninGotoInitialized = '1';
|
||
el.addEventListener('click', function (e) {
|
||
e.preventDefault();
|
||
var root = document.querySelector('[data-aksrt-signin]');
|
||
var btn = root ? root.querySelector('[data-signin-btn]') : null;
|
||
if (!btn) return;
|
||
try {
|
||
if (root && typeof root.scrollIntoView === 'function') {
|
||
root.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||
}
|
||
} catch (_) {}
|
||
if (!btn.disabled) {
|
||
btn.click();
|
||
} else {
|
||
btn.focus();
|
||
}
|
||
});
|
||
});
|
||
}
|
||
|
||
// ===== Forward-only affiliate revenue center =====
|
||
/* 推广链接由 PHP 直出,首屏即可见、可复制;这里只补异步才拿得到的
|
||
* 海报配置与佣金比例,取失败也不影响复制。 */
|
||
function initAffiliateRevenue() {
|
||
var root = document.querySelector('[data-aksrt-revenue-page]');
|
||
if (!root || root.dataset.affiliateBound) return;
|
||
root.dataset.affiliateBound = '1';
|
||
var input = root.querySelector('[data-affiliate-link]');
|
||
var copy = root.querySelector('[data-affiliate-copy]');
|
||
var url = input ? input.value : '';
|
||
if (copy) copy.disabled = !url;
|
||
api('/revenue/me').then(function (r) {
|
||
if (!r.ok) return;
|
||
var j = r.json || {};
|
||
var ratio = root.querySelector('[data-affiliate-ratio]'); if (ratio) ratio.textContent = String(j.current_ratio || 0) + '%';
|
||
initAffiliatePoster(root, url || j.url || '', j.poster || {}, j.site_name || document.title);
|
||
}).catch(function () { /* 海报与比例为增强项,静默失败即可。 */ });
|
||
if (copy) copy.addEventListener('click', function () {
|
||
if (!input || !input.value) return;
|
||
var done = function () { notyf('推广链接已复制', 'success', 3, 'aksrt-affiliate-copy-toast'); };
|
||
var fail = function () { input.focus(); input.select(); notyf('复制失败,请手动复制', 'danger', 3, 'aksrt-affiliate-copy-toast'); };
|
||
if (navigator.clipboard && navigator.clipboard.writeText) navigator.clipboard.writeText(input.value).then(done).catch(fail);
|
||
else { input.focus(); input.select(); try { document.execCommand('copy'); done(); } catch (e) { fail(); } }
|
||
});
|
||
}
|
||
|
||
// ===== Referral poster (canvas preview modal: hero image + copy + QR + link) =====
|
||
function initAffiliatePoster(root, url, poster, siteName) {
|
||
var button = root.querySelector('[data-affiliate-poster]');
|
||
var modal = document.querySelector('[data-aksrt-affiliate-poster-modal]');
|
||
var stage = document.querySelector('[data-affiliate-poster-stage]');
|
||
var download = document.querySelector('[data-affiliate-poster-download]');
|
||
if (!button || !modal) return;
|
||
if (!url || !poster || poster.enabled !== true) return;
|
||
button.hidden = false;
|
||
[].forEach.call(modal.querySelectorAll('[data-affiliate-poster-close]'), function (closer) {
|
||
closer.addEventListener('click', function () { closeModal(modal); });
|
||
});
|
||
var canvas = null;
|
||
if (download) {
|
||
download.addEventListener('click', function () {
|
||
if (!canvas) return;
|
||
try {
|
||
var link = document.createElement('a');
|
||
link.download = 'referral-poster.png';
|
||
link.href = canvas.toDataURL('image/png');
|
||
document.body.appendChild(link);
|
||
link.click();
|
||
link.remove();
|
||
} catch (err) {
|
||
/* Canvas is same-origin generated; download can only fail if the browser blocks it. */
|
||
}
|
||
});
|
||
}
|
||
button.addEventListener('click', function () {
|
||
if (typeof window.qrcode !== 'function') {
|
||
if (stage) { stage.innerHTML = '<p class="muted-2-color em09">' + '二维码组件未加载。' + '</p>'; }
|
||
openModal(modal);
|
||
return;
|
||
}
|
||
if (stage) { stage.innerHTML = '<p class="muted-2-color em09">' + '正在生成海报…' + '</p>'; }
|
||
if (download) download.hidden = true;
|
||
openModal(modal);
|
||
button.disabled = true;
|
||
buildPosterCanvas(url, poster, siteName).then(function (built) {
|
||
canvas = built;
|
||
if (stage) { stage.innerHTML = ''; stage.appendChild(built); }
|
||
if (download) download.hidden = false;
|
||
}).catch(function () {
|
||
if (stage) { stage.innerHTML = '<p class="muted-2-color em09">' + '海报生成失败,请重试。' + '</p>'; }
|
||
}).finally(function () {
|
||
button.disabled = false;
|
||
});
|
||
});
|
||
}
|
||
|
||
// 参考主题 poster-share.js 布局 1:1:768×1168,顶部主图占上半屏,
|
||
// 左上角日期,标题竖蓝线 + 标题,正文,底部带边框盒(logo/站名 + 二维码 + 链接)。
|
||
function buildPosterCanvas(url, poster, siteName) {
|
||
var width = 768, height = 1168;
|
||
var canvas = document.createElement('canvas');
|
||
canvas.width = width;
|
||
canvas.height = height;
|
||
var ctx = canvas.getContext('2d');
|
||
ctx.fillStyle = '#ffffff';
|
||
ctx.fillRect(0, 0, width, height);
|
||
var loadImage = null;
|
||
if (poster.image) {
|
||
loadImage = new Promise(function (resolve) {
|
||
var image = new Image();
|
||
image.crossOrigin = 'anonymous';
|
||
image.onload = function () { resolve(image); };
|
||
image.onerror = function () { resolve(null); };
|
||
image.src = poster.image;
|
||
});
|
||
} else {
|
||
loadImage = Promise.resolve(null);
|
||
}
|
||
return loadImage.then(function (image) {
|
||
var imgHeight = height / 2; // 参考主题 rebate(非 product):顶部主图占上半屏。
|
||
// 顶部主图(cover 裁切)或纯色占位。
|
||
if (image && image.width && image.height) {
|
||
var rect = coverRect(width, imgHeight, image.width, image.height);
|
||
ctx.drawImage(image, rect.sx, rect.sy, rect.sWidth, rect.sHeight, 0, 0, width, imgHeight);
|
||
} else {
|
||
ctx.fillStyle = '#0f172a';
|
||
ctx.fillRect(0, 0, width, imgHeight);
|
||
}
|
||
// 左上角日期(白字,覆盖在主图上)。
|
||
var now = new Date();
|
||
var week = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'];
|
||
ctx.textBaseline = 'top';
|
||
ctx.textAlign = 'left';
|
||
ctx.fillStyle = 'rgba(255,255,255,0.95)';
|
||
ctx.font = 'bold 35px Arial, sans-serif';
|
||
ctx.fillText(week[now.getDay()], 40, 35);
|
||
ctx.font = '28px Arial, sans-serif';
|
||
ctx.fillText((now.getMonth() + 1) + ' / ' + now.getDate(), 40, 88);
|
||
// 标题竖蓝线。
|
||
ctx.strokeStyle = '#00a1ff';
|
||
ctx.lineWidth = 7;
|
||
ctx.lineCap = 'round';
|
||
ctx.beginPath();
|
||
ctx.moveTo(40, imgHeight + 38);
|
||
ctx.lineTo(40, imgHeight + 74);
|
||
ctx.stroke();
|
||
// 标题(粗体,最多 2 行)。
|
||
ctx.fillStyle = '#424242';
|
||
ctx.font = 'bold 35px Arial, sans-serif';
|
||
var titleLines = wrapText(ctx, String(poster.title || siteName || ''), width - 100).slice(0, 2);
|
||
var cursorY = imgHeight + 40;
|
||
for (var t = 0; t < titleLines.length; t++) {
|
||
ctx.fillText(titleLines[t], 60, cursorY);
|
||
cursorY += 52;
|
||
}
|
||
// 正文(简介,最多 3 行)。
|
||
var description = String(poster.description || '');
|
||
if (description) {
|
||
ctx.fillStyle = '#585858';
|
||
ctx.font = '25px Arial, sans-serif';
|
||
var lines = wrapText(ctx, description, width - 80).slice(0, 3);
|
||
var contentY = imgHeight + 160;
|
||
for (var i = 0; i < lines.length; i++) {
|
||
ctx.fillText(lines[i], 40, contentY);
|
||
contentY += 38;
|
||
}
|
||
}
|
||
// 底部带边框盒。
|
||
ctx.strokeStyle = 'rgba(122,122,122,0.2)';
|
||
ctx.lineWidth = 2;
|
||
ctx.strokeRect(30, height - 230, width - 60, 200);
|
||
// 站点名(logo 位,左侧)。
|
||
ctx.fillStyle = '#424242';
|
||
ctx.font = 'bold 30px Arial, sans-serif';
|
||
ctx.fillText(String(siteName || ''), 80, height - 175);
|
||
ctx.fillStyle = '#94a3b8';
|
||
ctx.font = '20px Arial, sans-serif';
|
||
ctx.fillText('长按识别二维码访问', 80, height - 130);
|
||
// 二维码(右下)。
|
||
var qr = window.qrcode(0, 'M');
|
||
qr.addData(url);
|
||
qr.make();
|
||
var count = qr.getModuleCount();
|
||
var qrBox = 120;
|
||
var cell = qrBox / count;
|
||
var qrX = width - 200, qrY = height - 200;
|
||
ctx.fillStyle = '#0f172a';
|
||
for (var r = 0; r < count; r++) {
|
||
for (var c = 0; c < count; c++) {
|
||
if (qr.isDark(r, c)) {
|
||
ctx.fillRect(qrX + Math.floor(c * cell), qrY + Math.floor(r * cell), Math.ceil(cell), Math.ceil(cell));
|
||
}
|
||
}
|
||
}
|
||
// 底部链接(居中)。
|
||
ctx.textAlign = 'center';
|
||
ctx.fillStyle = '#b4b4b4';
|
||
ctx.font = '22px Arial, sans-serif';
|
||
ctx.fillText(String(url).slice(0, 46), width / 2, height - 46);
|
||
ctx.textAlign = 'left';
|
||
return canvas;
|
||
});
|
||
}
|
||
|
||
// cover 裁切:把源图按短边铺满目标框并居中裁切。
|
||
function coverRect(boxW, boxH, srcW, srcH) {
|
||
var sx = 0, sy = 0, sWidth = srcW, sHeight = srcH;
|
||
if (srcW / srcH > boxW / boxH) {
|
||
sWidth = (boxW * srcH) / boxH;
|
||
sx = (srcW - sWidth) / 2;
|
||
} else {
|
||
sHeight = (boxH * srcW) / boxW;
|
||
sy = (srcH - sHeight) / 2;
|
||
}
|
||
return { sx: sx, sy: sy, sWidth: sWidth, sHeight: sHeight };
|
||
}
|
||
|
||
function wrapText(ctx, text, maxWidth) {
|
||
var lines = [];
|
||
var current = '';
|
||
for (var i = 0; i < text.length; i++) {
|
||
if (ctx.measureText(current + text[i]).width > maxWidth && current) {
|
||
lines.push(current);
|
||
current = text[i];
|
||
} else {
|
||
current += text[i];
|
||
}
|
||
}
|
||
if (current) lines.push(current);
|
||
return lines;
|
||
}
|
||
|
||
window.AKSRTAffiliateRevenueInit = initAffiliateRevenue;
|
||
|
||
// ===== VIP 开通 =====
|
||
function initVipShop() {
|
||
var root = document.querySelector('[data-aksrt-vip-shop]');
|
||
if (!root) return;
|
||
var tip = root.querySelector('[data-aksrt-vip-tip]');
|
||
var busy = false;
|
||
var coordinator = window.AksrtVipPurchaseCoordinator;
|
||
|
||
function setTip(text, isError) {
|
||
if (!tip) return;
|
||
tip.textContent = text || '';
|
||
tip.classList.toggle('is-error', !!isError);
|
||
}
|
||
|
||
function selectedMethod() {
|
||
var fixed = root.querySelector('[data-aksrt-vip-method-fixed]');
|
||
if (fixed && fixed.value) return fixed.value;
|
||
var checked = root.querySelector('input[name="aksrt_vip_pay_method"]:checked');
|
||
if (checked) return checked.value;
|
||
var first = root.querySelector('input[name="aksrt_vip_pay_method"]');
|
||
return first ? first.value : 'balance';
|
||
}
|
||
|
||
root.addEventListener('click', function (e) {
|
||
var btn = e.target.closest('[data-aksrt-vip-buy]');
|
||
if (!btn || busy) return;
|
||
e.preventDefault();
|
||
var packageId = parseInt(btn.getAttribute('data-package-id') || '0', 10);
|
||
if (!packageId) return;
|
||
busy = true;
|
||
btn.disabled = true;
|
||
btn.classList.add('is-loading');
|
||
setTip('正在创建订单…');
|
||
|
||
coordinator.start({
|
||
packageId: packageId,
|
||
paymentMethod: selectedMethod(),
|
||
tags: ['vip', 'orders'],
|
||
onPending: function (data) {
|
||
busy = false;
|
||
btn.disabled = false;
|
||
btn.classList.remove('is-loading');
|
||
if (data.payment_url) window.open(data.payment_url, '_blank');
|
||
setTip(data.message || '请完成支付,支付成功后将自动刷新。');
|
||
},
|
||
onSuccess: function (result) {
|
||
busy = false;
|
||
btn.disabled = false;
|
||
btn.classList.remove('is-loading');
|
||
setTip((result.data && result.data.message) || '开通成功,正在刷新…');
|
||
window.location.reload();
|
||
},
|
||
onFailure: function () {
|
||
busy = false;
|
||
btn.disabled = false;
|
||
btn.classList.remove('is-loading');
|
||
setTip('支付未完成,请重试。', true);
|
||
},
|
||
onTimeout: function () {
|
||
busy = false;
|
||
btn.disabled = false;
|
||
btn.classList.remove('is-loading');
|
||
setTip('支付状态暂未确认,请稍后在订单中心查看。', true);
|
||
},
|
||
onError: function (response) {
|
||
busy = false;
|
||
btn.disabled = false;
|
||
btn.classList.remove('is-loading');
|
||
setTip((response.json && response.json.message) || '开通失败', true);
|
||
}
|
||
});
|
||
});
|
||
}
|
||
|
||
// ===== 卡密兑换(三个购买弹窗共用的卡密渠道提交) =====
|
||
function redeemCardPass(type, card, password, keyOwner, toastId) {
|
||
var body = { password: password, type: type };
|
||
if (card) body.card = card;
|
||
return api('/pay/card-pass/redeem', {
|
||
method: 'POST',
|
||
headers: { 'Idempotency-Key': operationKey(keyOwner) },
|
||
body: JSON.stringify(body)
|
||
}).then(function (r) {
|
||
if (!r.ok || (r.json && r.json.code && !r.json.success)) {
|
||
return { ok: false, message: (r.json && r.json.message) || '兑换失败' };
|
||
}
|
||
var data = (r.json && r.json.data) ? r.json.data : r.json;
|
||
resetOperationKey(keyOwner);
|
||
if (type === 'vip' && window.AksrtModalRuntime && typeof window.AksrtModalRuntime.invalidate === 'function') {
|
||
window.AksrtModalRuntime.invalidate(['vip', 'orders']);
|
||
}
|
||
notyf((data && data.message) || '兑换成功,正在刷新…', 'success', 5, toastId);
|
||
window.setTimeout(function () { window.location.reload(); }, 800);
|
||
return { ok: true };
|
||
}).catch(function () {
|
||
return { ok: false, message: '网络错误,请重试' };
|
||
});
|
||
}
|
||
|
||
// ===== 余额充值 / 转账 / 余额兑积分 =====
|
||
function openModal(el) {
|
||
if (!el) return;
|
||
el.hidden = false;
|
||
el.classList.add('is-open');
|
||
document.body.classList.add('uc-modal-open');
|
||
}
|
||
function closeModal(el) {
|
||
if (!el) return;
|
||
if (activePoll && typeof activePoll.abort === 'function') {
|
||
activePoll.abort();
|
||
notyf('已停止查询支付状态', 'warning', 3, 'aksrt-balance-charge-payment-toast');
|
||
}
|
||
activePoll = null;
|
||
el.hidden = true;
|
||
el.classList.remove('is-open');
|
||
if (!document.querySelector('.uc-modal.is-open')) {
|
||
document.body.classList.remove('uc-modal-open');
|
||
}
|
||
}
|
||
function pollPaidOrder(orderNumber, tags) {
|
||
var coordinator = window.AksrtVipPurchaseCoordinator;
|
||
if (!orderNumber) return;
|
||
notyf('等待支付结果…', 'load', '', 'aksrt-balance-charge-payment-toast');
|
||
var handle = coordinator.poll(orderNumber, {
|
||
tags: tags || ['credits', 'orders'],
|
||
onTerminal: function (result) {
|
||
activePoll = null;
|
||
if (result.success) {
|
||
notyf('支付成功,正在刷新…', 'success', 5, 'aksrt-balance-charge-payment-toast');
|
||
refreshAssetBalances();
|
||
window.setTimeout(function () { window.location.reload(); }, 800);
|
||
return;
|
||
}
|
||
var message = result.data && result.data.message ? result.data.message : '支付未完成,请重试';
|
||
notyf(message, 'danger', 5, 'aksrt-balance-charge-payment-toast');
|
||
},
|
||
onTimeout: function () {
|
||
activePoll = null;
|
||
notyf('支付状态暂未确认,请稍后在订单中心查看', 'danger', 5, 'aksrt-balance-charge-payment-toast');
|
||
}
|
||
});
|
||
return handle;
|
||
}
|
||
|
||
function initBalanceCharge() {
|
||
var modal = document.querySelector('[data-aksrt-charge-modal]');
|
||
if (!modal || modal.dataset.aksrtChargeInitialized === '1') return;
|
||
modal.dataset.aksrtChargeInitialized = '1';
|
||
var form = modal.querySelector('[data-aksrt-charge-form]');
|
||
var busy = false;
|
||
var chargeBackdrop = null;
|
||
|
||
// 开关自包含:不复依赖 jQuery/Bootstrap modal 运行时,行为与视觉
|
||
// 复刻 Bootstrap 3(.in 显隐 + .modal-backdrop + body.modal-open + ESC)。
|
||
function openChargeModal() {
|
||
if (modal.classList.contains('in')) return;
|
||
// 兼容旧缓存标记(uc-modal 带 hidden 属性)
|
||
modal.removeAttribute('hidden');
|
||
// 打开态用 flex 而非 block:壳的 .flex.jc 负责双轴居中,
|
||
// .modal.flex .modal-dialog{margin:0} 在 block 下会把对话框顶到左上角。
|
||
modal.style.display = 'flex';
|
||
void modal.offsetWidth; // 强制 reflow,让 .fade 过渡从初始态开始
|
||
modal.classList.add('in');
|
||
if (!chargeBackdrop) {
|
||
chargeBackdrop = document.createElement('div');
|
||
chargeBackdrop.className = 'modal-backdrop fade in';
|
||
chargeBackdrop.setAttribute('data-aksrt-charge-backdrop', '1');
|
||
document.body.appendChild(chargeBackdrop);
|
||
}
|
||
document.body.classList.add('modal-open');
|
||
}
|
||
function closeChargeModal() {
|
||
if (!modal.classList.contains('in')) return;
|
||
if (activePoll && typeof activePoll.abort === 'function') {
|
||
activePoll.abort();
|
||
notyf('已停止查询支付状态', 'warning', 3, 'aksrt-balance-charge-payment-toast');
|
||
}
|
||
activePoll = null;
|
||
modal.classList.remove('in');
|
||
document.body.classList.remove('modal-open');
|
||
if (chargeBackdrop && chargeBackdrop.parentNode) {
|
||
chargeBackdrop.parentNode.removeChild(chargeBackdrop);
|
||
}
|
||
chargeBackdrop = null;
|
||
// 等过渡结束再撤 display,复刻 Bootstrap 的 fade-out 收尾
|
||
window.setTimeout(function () {
|
||
if (!modal.classList.contains('in')) {
|
||
modal.style.display = '';
|
||
}
|
||
}, 300);
|
||
}
|
||
document.querySelectorAll('[data-aksrt-charge-open]').forEach(function (btn) {
|
||
btn.addEventListener('click', openChargeModal);
|
||
});
|
||
modal.addEventListener('click', function (e) {
|
||
// 点遮罩层(.modal 自身)或关闭按钮(data-dismiss="modal")
|
||
if (e.target === modal || (e.target.closest && e.target.closest('[data-dismiss="modal"]'))) {
|
||
closeChargeModal();
|
||
}
|
||
});
|
||
document.addEventListener('keydown', function (e) {
|
||
if (e.key === 'Escape' && modal.classList.contains('in')) {
|
||
closeChargeModal();
|
||
}
|
||
});
|
||
// Zibll data-for/data-value 选中模型:余额档位(.vip-product.product-box)
|
||
// 选中变化不产生 change 事件(隐藏 input 由程序赋值),需显式重置幂等键。
|
||
var customInput = modal.querySelector('input[name="custom_price"]');
|
||
function selectProduct(value) {
|
||
modal.querySelectorAll('[data-for="balance_product"]').forEach(function (el) {
|
||
el.classList.toggle('active', el.getAttribute('data-value') === value);
|
||
});
|
||
var hidden = modal.querySelector('input[name="balance_product"]');
|
||
if (hidden) hidden.value = value;
|
||
// 选中预设档位即代表放弃自定义金额:清空输入框,否则框里残留的数字会在
|
||
// 提交时按「合法自定义金额」把 package 顶回 custom,静默覆盖刚点的档位。
|
||
if (value !== 'custom' && customInput) {
|
||
customInput.value = '';
|
||
}
|
||
if (form) resetOperationKey(form);
|
||
}
|
||
modal.querySelectorAll('[data-for="balance_product"]').forEach(function (el) {
|
||
el.addEventListener('click', function () {
|
||
selectProduct(el.getAttribute('data-value'));
|
||
});
|
||
});
|
||
if (customInput) {
|
||
customInput.addEventListener('input', function () {
|
||
if (String(customInput.value || '').trim() !== '') {
|
||
selectProduct('custom');
|
||
}
|
||
});
|
||
}
|
||
// 支付方式(.payment-method-radio);卡密渠道选中时隐藏金额选择、
|
||
// 显示卡密输入盒(data-controller="payment_method" 契约与参考主题一致)。
|
||
function cardPassBox() {
|
||
return modal.querySelector('[data-controller="payment_method"][data-value="card_pass"]');
|
||
}
|
||
function applyMethodVisibility(value) {
|
||
var cardBox = cardPassBox();
|
||
if (cardBox) cardBox.style.display = value === 'card_pass' ? '' : 'none';
|
||
var chargeBoxes = modal.querySelectorAll('.charge-box');
|
||
chargeBoxes.forEach(function (el) { el.style.display = value === 'card_pass' ? 'none' : ''; });
|
||
}
|
||
function selectMethod(value) {
|
||
modal.querySelectorAll('[data-for="payment_method"]').forEach(function (el) {
|
||
el.classList.toggle('active', el.getAttribute('data-value') === value);
|
||
});
|
||
var hidden = modal.querySelector('input[name="payment_method"]');
|
||
if (hidden) hidden.value = value;
|
||
applyMethodVisibility(value);
|
||
if (form) resetOperationKey(form);
|
||
}
|
||
modal.querySelectorAll('[data-for="payment_method"]').forEach(function (el) {
|
||
el.addEventListener('click', function () {
|
||
selectMethod(el.getAttribute('data-value'));
|
||
});
|
||
});
|
||
applyMethodVisibility((modal.querySelector('input[name="payment_method"]') || {}).value || '');
|
||
if (!form) return;
|
||
resetOperationKeyOnChange(form);
|
||
form.addEventListener('submit', function (e) {
|
||
e.preventDefault();
|
||
if (busy) return;
|
||
var methodEl = form.querySelector('input[name="payment_method"]');
|
||
var method = methodEl ? methodEl.value : '';
|
||
if (!method) {
|
||
notyf('暂无可用支付方式', 'danger');
|
||
return;
|
||
}
|
||
if (method === 'card_pass') {
|
||
var cardEl = form.querySelector('input[name="card_pass[card]"]');
|
||
var pwEl = form.querySelector('input[name="card_pass[password]"]');
|
||
var password = pwEl ? String(pwEl.value || '').trim() : '';
|
||
if (!password) {
|
||
notyf('请输入卡密', 'danger');
|
||
if (pwEl) pwEl.focus();
|
||
return;
|
||
}
|
||
busy = true;
|
||
notyf('正在兑换…', 'load', '', 'aksrt-balance-charge-toast');
|
||
redeemCardPass('balance', cardEl ? String(cardEl.value || '').trim() : '', password, form, 'aksrt-balance-charge-toast')
|
||
.then(function (result) {
|
||
busy = false;
|
||
if (!result.ok) notyf(result.message, 'danger', 5, 'aksrt-balance-charge-toast');
|
||
});
|
||
return;
|
||
}
|
||
var pkgInput = form.querySelector('input[name="balance_product"]');
|
||
var pkg = pkgInput ? pkgInput.value : 'custom';
|
||
var customEl = form.querySelector('input[name="custom_price"]');
|
||
var custom = customEl ? String(customEl.value || '').trim() : '';
|
||
var validCustom = /^(0|[1-9]\d*)(?:\.\d{1,2})?$/.test(custom) && custom !== '0' && custom !== '0.0' && custom !== '0.00';
|
||
if (validCustom) {
|
||
pkg = 'custom';
|
||
selectProduct('custom');
|
||
}
|
||
if ((!pkg || pkg === 'custom') && !validCustom) {
|
||
notyf('请选择或输入充值金额', 'danger');
|
||
return;
|
||
}
|
||
busy = true;
|
||
notyf('正在创建充值订单…', 'load', '', 'aksrt-balance-charge-toast');
|
||
api('/pay/balance/charge', {
|
||
method: 'POST',
|
||
headers: { 'Idempotency-Key': operationKey(form) },
|
||
body: JSON.stringify({
|
||
package: pkg,
|
||
// 预设档位下该字段为空串:服务端仅在 package=custom 时消费它,
|
||
// 空串表示「未使用自定义金额」,不是非法值。
|
||
custom_price: custom,
|
||
payment_method: method
|
||
})
|
||
}).then(function (r) {
|
||
busy = false;
|
||
if (!r.ok || (r.json && r.json.code && !r.json.success)) {
|
||
notyf((r.json && r.json.message) || '充值失败', 'danger', 5, 'aksrt-balance-charge-toast');
|
||
return;
|
||
}
|
||
var data = (r.json && r.json.data) ? r.json.data : r.json;
|
||
resetOperationKey(form);
|
||
if (data && data.redirect && (data.payment_url || data.qr_content)) {
|
||
if (data.payment_url) window.open(data.payment_url, '_blank');
|
||
notyf(data.message || '请完成支付,支付成功后将自动刷新。', 'info', 5, 'aksrt-balance-charge-toast');
|
||
if (data.order_number) activePoll = pollPaidOrder(data.order_number, ['credits', 'orders']);
|
||
return;
|
||
}
|
||
notyf((data && data.message) || '充值成功,正在刷新…', 'success', 5, 'aksrt-balance-charge-toast');
|
||
refreshAssetBalances(data);
|
||
window.setTimeout(function () { window.location.reload(); }, 800);
|
||
}).catch(function () {
|
||
busy = false;
|
||
notyf('网络错误,请重试', 'danger', 5, 'aksrt-balance-charge-toast');
|
||
});
|
||
});
|
||
}
|
||
|
||
function initTransfer() {
|
||
var modal = document.querySelector('[data-aksrt-transfer-modal]');
|
||
if (!modal || modal.dataset.aksrtTransferInitialized === '1') return;
|
||
modal.dataset.aksrtTransferInitialized = '1';
|
||
var form = modal.querySelector('[data-aksrt-transfer-form]');
|
||
var results = modal.querySelector('[data-aksrt-transfer-results]');
|
||
var selected = modal.querySelector('[data-aksrt-transfer-selected]');
|
||
var recipientInput = modal.querySelector('[data-aksrt-transfer-recipient]');
|
||
var typeInput = modal.querySelector('[data-aksrt-transfer-type]');
|
||
var title = modal.querySelector('[data-aksrt-transfer-title]');
|
||
var amountLabel = modal.querySelector('[data-aksrt-transfer-amount-label]');
|
||
var balanceInfo = modal.querySelector('[data-aksrt-transfer-balance-info]');
|
||
var pointsInfo = modal.querySelector('[data-aksrt-transfer-points-info]');
|
||
var pointsTransferMin = 1;
|
||
var searchTimer = null;
|
||
var busy = false;
|
||
document.querySelectorAll('[data-aksrt-transfer-open]').forEach(function (btn) {
|
||
btn.addEventListener('click', function () {
|
||
var type = btn.getAttribute('data-aksrt-transfer-open') || 'balance';
|
||
pointsTransferMin = type === 'points'
|
||
? Math.max(1, parseInt(btn.getAttribute('data-aksrt-transfer-min') || '1', 10) || 1)
|
||
: 1;
|
||
if (typeInput) typeInput.value = type;
|
||
if (title) title.textContent = type === 'points' ? '积分转账' : '余额转账';
|
||
var amountEl = form ? form.querySelector('[data-aksrt-transfer-amount]') : null;
|
||
if (amountEl) {
|
||
amountEl.min = type === 'points' ? String(pointsTransferMin) : '0.01';
|
||
amountEl.step = type === 'points' ? '1' : '0.01';
|
||
amountEl.inputMode = type === 'points' ? 'numeric' : 'decimal';
|
||
amountEl.value = '';
|
||
}
|
||
if (amountLabel) amountLabel.textContent = type === 'points' ? '积分' : '金额';
|
||
if (balanceInfo) balanceInfo.hidden = type === 'points';
|
||
if (pointsInfo) pointsInfo.hidden = type !== 'points';
|
||
if (results) results.innerHTML = '';
|
||
if (selected) selected.textContent = '';
|
||
if (recipientInput) recipientInput.value = '';
|
||
openModal(modal);
|
||
});
|
||
});
|
||
modal.querySelectorAll('[data-aksrt-transfer-close]').forEach(function (btn) {
|
||
btn.addEventListener('click', function () { closeModal(modal); });
|
||
});
|
||
var searchEl = modal.querySelector('[data-aksrt-transfer-search]');
|
||
if (searchEl) {
|
||
searchEl.addEventListener('input', function () {
|
||
var q = String(searchEl.value || '').trim();
|
||
if (searchTimer) clearTimeout(searchTimer);
|
||
if (q.length < 1) {
|
||
if (results) results.innerHTML = '';
|
||
return;
|
||
}
|
||
searchTimer = setTimeout(function () {
|
||
api('/pay/transfer/search?s=' + encodeURIComponent(q)).then(function (r) {
|
||
if (!results) return;
|
||
results.innerHTML = '';
|
||
if (!r.ok) {
|
||
notyf((r.json && r.json.message) || '用户搜索失败', 'danger');
|
||
return;
|
||
}
|
||
var data = (r.json && r.json.data) ? r.json.data : r.json;
|
||
var users = (data && data.users) || [];
|
||
if (!users.length) {
|
||
results.innerHTML = '<div class="muted-2-color em09">未找到相关用户</div>';
|
||
return;
|
||
}
|
||
users.forEach(function (u) {
|
||
var item = document.createElement('button');
|
||
item.type = 'button';
|
||
item.className = 'uc-transfer-user';
|
||
var avatar = document.createElement('img');
|
||
avatar.className = 'uc-transfer-user__avatar';
|
||
avatar.alt = '';
|
||
avatar.src = String(u.avatar || '');
|
||
item.appendChild(avatar);
|
||
var displayName = document.createElement('span');
|
||
displayName.textContent = String(u.display_name || u.user_login || '');
|
||
item.appendChild(displayName);
|
||
var login = document.createElement('span');
|
||
login.className = 'muted-2-color em09';
|
||
login.textContent = '@' + String(u.user_login || '');
|
||
item.appendChild(login);
|
||
item.addEventListener('click', function () {
|
||
if (recipientInput) recipientInput.value = String(u.id);
|
||
if (selected) selected.textContent = '已选择:' + (u.display_name || u.user_login);
|
||
results.innerHTML = '';
|
||
});
|
||
results.appendChild(item);
|
||
});
|
||
});
|
||
}, 280);
|
||
});
|
||
}
|
||
if (!form) return;
|
||
resetOperationKeyOnChange(form);
|
||
form.addEventListener('submit', function (e) {
|
||
e.preventDefault();
|
||
if (busy) return;
|
||
var type = typeInput ? typeInput.value : 'balance';
|
||
var recipient = recipientInput ? parseInt(recipientInput.value || '0', 10) : 0;
|
||
var amountEl = form.querySelector('[data-aksrt-transfer-amount]');
|
||
var amount = amountEl ? amountEl.value : 0;
|
||
if (!recipient) {
|
||
notyf('请选择收款用户', 'danger');
|
||
return;
|
||
}
|
||
var pointsAmount = /^[1-9]\d*$/.test(String(amount)) ? BigInt(String(amount)) : 0n;
|
||
var cashAmount = balanceMinor(amount);
|
||
var validAmount = type === 'points'
|
||
? pointsAmount >= BigInt(pointsTransferMin) && pointsAmount <= BigInt(Number.MAX_SAFE_INTEGER)
|
||
: cashAmount !== null && cashAmount > 0n;
|
||
if (!validAmount) {
|
||
notyf('请输入有效金额', 'danger');
|
||
return;
|
||
}
|
||
busy = true;
|
||
notyf('正在转账…', 'load', '', 'aksrt-balance-transfer-toast');
|
||
api('/pay/transfer', {
|
||
method: 'POST',
|
||
headers: { 'Idempotency-Key': operationKey(form) },
|
||
body: JSON.stringify({
|
||
type: type,
|
||
recipient: recipient,
|
||
amount: type === 'points' ? Number(pointsAmount) : String(amount)
|
||
})
|
||
}).then(function (r) {
|
||
busy = false;
|
||
if (!r.ok || (r.json && r.json.code && !r.json.success)) {
|
||
notyf((r.json && r.json.message) || '转账失败', 'danger', 5, 'aksrt-balance-transfer-toast');
|
||
return;
|
||
}
|
||
var data = (r.json && r.json.data) ? r.json.data : r.json;
|
||
resetOperationKey(form);
|
||
notyf((data && data.message) || '转账成功,正在刷新…', 'success', 5, 'aksrt-balance-transfer-toast');
|
||
refreshAssetBalances();
|
||
window.setTimeout(function () { window.location.reload(); }, 800);
|
||
}).catch(function () {
|
||
busy = false;
|
||
notyf('网络错误,请重试', 'danger', 5, 'aksrt-balance-transfer-toast');
|
||
});
|
||
});
|
||
}
|
||
|
||
// ===== 购买积分(Zibll points-pay modal 1:1:商品/自定义 + 余额/卡密支付) =====
|
||
function initPointsPay() {
|
||
var modal = document.querySelector('[data-aksrt-points-pay-modal]');
|
||
if (!modal || modal.dataset.aksrtPointsPayInitialized === '1') return;
|
||
modal.dataset.aksrtPointsPayInitialized = '1';
|
||
var form = modal.querySelector('[data-aksrt-points-pay-form]');
|
||
var busy = false;
|
||
var backdrop = null;
|
||
|
||
function openPointsModal() {
|
||
if (modal.classList.contains('in')) return;
|
||
modal.style.display = 'flex';
|
||
void modal.offsetWidth;
|
||
modal.classList.add('in');
|
||
if (!backdrop) {
|
||
backdrop = document.createElement('div');
|
||
backdrop.className = 'modal-backdrop fade in';
|
||
backdrop.setAttribute('data-aksrt-points-backdrop', '1');
|
||
document.body.appendChild(backdrop);
|
||
}
|
||
document.body.classList.add('modal-open');
|
||
}
|
||
function closePointsModal() {
|
||
if (!modal.classList.contains('in')) return;
|
||
modal.classList.remove('in');
|
||
document.body.classList.remove('modal-open');
|
||
if (backdrop && backdrop.parentNode) backdrop.parentNode.removeChild(backdrop);
|
||
backdrop = null;
|
||
window.setTimeout(function () {
|
||
if (!modal.classList.contains('in')) modal.style.display = '';
|
||
}, 300);
|
||
}
|
||
document.querySelectorAll('[data-aksrt-points-pay-open]').forEach(function (btn) {
|
||
btn.addEventListener('click', openPointsModal);
|
||
});
|
||
modal.addEventListener('click', function (e) {
|
||
if (e.target === modal || (e.target.closest && e.target.closest('[data-dismiss="modal"]'))) {
|
||
closePointsModal();
|
||
}
|
||
});
|
||
document.addEventListener('keydown', function (e) {
|
||
if (e.key === 'Escape' && modal.classList.contains('in')) closePointsModal();
|
||
});
|
||
|
||
// Zibll data-for/data-value 选中模型:积分商品档位。
|
||
function selectProduct(value) {
|
||
modal.querySelectorAll('[data-for="points_product"]').forEach(function (el) {
|
||
el.classList.toggle('active', el.getAttribute('data-value') === value);
|
||
});
|
||
var hidden = modal.querySelector('input[name="points_product"]');
|
||
if (hidden) hidden.value = value;
|
||
if (form) resetOperationKey(form);
|
||
}
|
||
modal.querySelectorAll('[data-for="points_product"]').forEach(function (el) {
|
||
el.addEventListener('click', function () {
|
||
selectProduct(el.getAttribute('data-value'));
|
||
});
|
||
});
|
||
var customInput = modal.querySelector('input[name="points_custom"]');
|
||
if (customInput) {
|
||
customInput.addEventListener('input', function () {
|
||
if (String(customInput.value || '').trim() !== '') selectProduct('custom');
|
||
});
|
||
}
|
||
// 支付方式(余额/卡密);选中卡密时隐藏商品选择、显示卡密输入盒。
|
||
function cardPassBox() {
|
||
return modal.querySelector('[data-controller="payment_method"][data-value="card_pass"]');
|
||
}
|
||
function applyMethodVisibility(value) {
|
||
var cardBox = cardPassBox();
|
||
if (cardBox) cardBox.style.display = value === 'card_pass' ? '' : 'none';
|
||
modal.querySelectorAll('.charge-box').forEach(function (el) {
|
||
el.style.display = value === 'card_pass' ? 'none' : '';
|
||
});
|
||
}
|
||
function selectMethod(value) {
|
||
modal.querySelectorAll('[data-for="payment_method"]').forEach(function (el) {
|
||
el.classList.toggle('active', el.getAttribute('data-value') === value);
|
||
});
|
||
var hidden = modal.querySelector('input[name="payment_method"]');
|
||
if (hidden) hidden.value = value;
|
||
applyMethodVisibility(value);
|
||
if (form) resetOperationKey(form);
|
||
}
|
||
modal.querySelectorAll('[data-for="payment_method"]').forEach(function (el) {
|
||
el.addEventListener('click', function () {
|
||
selectMethod(el.getAttribute('data-value'));
|
||
});
|
||
});
|
||
applyMethodVisibility((modal.querySelector('input[name="payment_method"]') || {}).value || '');
|
||
if (!form) return;
|
||
resetOperationKeyOnChange(form);
|
||
form.addEventListener('submit', function (e) {
|
||
e.preventDefault();
|
||
if (busy) return;
|
||
var methodEl = form.querySelector('input[name="payment_method"]');
|
||
var method = methodEl ? methodEl.value : '';
|
||
if (!method) {
|
||
notyf('暂无可用支付方式', 'danger');
|
||
return;
|
||
}
|
||
if (method === 'card_pass') {
|
||
var cardEl = form.querySelector('input[name="card_pass[card]"]');
|
||
var pwEl = form.querySelector('input[name="card_pass[password]"]');
|
||
var password = pwEl ? String(pwEl.value || '').trim() : '';
|
||
if (!password) {
|
||
notyf('请输入卡密', 'danger');
|
||
if (pwEl) pwEl.focus();
|
||
return;
|
||
}
|
||
busy = true;
|
||
notyf('正在兑换…', 'load', '', 'aksrt-balance-points-toast');
|
||
redeemCardPass('credits', cardEl ? String(cardEl.value || '').trim() : '', password, form, 'aksrt-balance-points-toast')
|
||
.then(function (result) {
|
||
busy = false;
|
||
if (!result.ok) notyf(result.message, 'danger', 5, 'aksrt-balance-points-toast');
|
||
});
|
||
return;
|
||
}
|
||
var pkgInput = form.querySelector('input[name="points_product"]');
|
||
var pkg = pkgInput ? pkgInput.value : 'custom';
|
||
var customEl = form.querySelector('input[name="points_custom"]');
|
||
var custom = customEl ? String(customEl.value || '').trim() : '';
|
||
var validCustom = /^[1-9]\d{0,8}$/.test(custom);
|
||
if (validCustom) {
|
||
pkg = 'custom';
|
||
selectProduct('custom');
|
||
}
|
||
if ((!pkg || pkg === 'custom') && !validCustom) {
|
||
notyf('请选择或输入需购买的积分', 'danger');
|
||
return;
|
||
}
|
||
busy = true;
|
||
notyf('正在购买积分…', 'load', '', 'aksrt-balance-points-toast');
|
||
api('/pay/balance/points-exchange', {
|
||
method: 'POST',
|
||
headers: { 'Idempotency-Key': operationKey(form) },
|
||
body: JSON.stringify({ product: pkg, custom: validCustom ? custom : '0' })
|
||
}).then(function (r) {
|
||
busy = false;
|
||
if (!r.ok || (r.json && r.json.code && !r.json.success)) {
|
||
notyf((r.json && r.json.message) || '购买失败', 'danger', 5, 'aksrt-balance-points-toast');
|
||
return;
|
||
}
|
||
var data = (r.json && r.json.data) ? r.json.data : r.json;
|
||
resetOperationKey(form);
|
||
notyf((data && data.message) || '购买成功,正在刷新…', 'success', 5, 'aksrt-balance-points-toast');
|
||
refreshAssetBalances(data);
|
||
window.setTimeout(function () { window.location.reload(); }, 800);
|
||
}).catch(function () {
|
||
busy = false;
|
||
notyf('网络错误,请重试', 'danger', 5, 'aksrt-balance-points-toast');
|
||
});
|
||
});
|
||
}
|
||
|
||
function initWithdraw() {
|
||
var modal = document.querySelector('[data-aksrt-withdraw-modal]');
|
||
if (!modal || modal.dataset.aksrtWithdrawInitialized === '1') return;
|
||
modal.dataset.aksrtWithdrawInitialized = '1';
|
||
var form = modal.querySelector('[data-aksrt-withdraw-form]');
|
||
var busy = false;
|
||
// 提现来源随打开按钮传入:commission 走佣金池,其余走余额钱包。
|
||
// 每个来源的每日次数/额度不同,按钮上带了各自的 min/remaining,
|
||
// 打开时覆盖到金额输入框,提交时把 source 一并回传。
|
||
document.querySelectorAll('[data-aksrt-withdraw-open]').forEach(function (btn) {
|
||
btn.addEventListener('click', function () {
|
||
var src = btn.getAttribute('data-aksrt-withdraw-source') || 'balance';
|
||
modal.dataset.aksrtWithdrawSource = src === 'commission' ? 'commission' : 'balance';
|
||
var amountEl = form ? form.querySelector('[data-aksrt-withdraw-amount]') : null;
|
||
if (amountEl) {
|
||
var min = btn.getAttribute('data-withdraw-min');
|
||
var remAmt = btn.getAttribute('data-withdraw-remaining-amount');
|
||
var remCnt = btn.getAttribute('data-withdraw-remaining-count');
|
||
if (min !== null) amountEl.setAttribute('data-withdraw-min', min);
|
||
if (remAmt !== null) amountEl.setAttribute('data-withdraw-remaining-amount', remAmt);
|
||
if (remCnt !== null) amountEl.setAttribute('data-withdraw-remaining-count', remCnt);
|
||
}
|
||
openModal(modal);
|
||
});
|
||
});
|
||
modal.querySelectorAll('[data-aksrt-withdraw-close]').forEach(function (btn) {
|
||
btn.addEventListener('click', function () { closeModal(modal); });
|
||
});
|
||
if (!form) return;
|
||
resetOperationKeyOnChange(form);
|
||
form.addEventListener('submit', function (e) {
|
||
e.preventDefault();
|
||
if (busy) return;
|
||
var amountEl = form.querySelector('[data-aksrt-withdraw-amount]');
|
||
var methodEl = form.querySelector('[data-aksrt-withdraw-method]');
|
||
var accountEl = form.querySelector('[data-aksrt-withdraw-account]');
|
||
var rawAmount = amountEl ? String(amountEl.value || '').trim() : '';
|
||
var amount = rawAmount;
|
||
var amountMinor = balanceMinor(amount);
|
||
var minAmount = amountEl ? balanceMinor(amountEl.getAttribute('data-withdraw-min') || '0') : 0n;
|
||
var remainingAmount = amountEl ? balanceMinor(amountEl.getAttribute('data-withdraw-remaining-amount') || '0') : 0n;
|
||
var remainingCount = amountEl ? Number(amountEl.getAttribute('data-withdraw-remaining-count') || '0') : 0;
|
||
var method = methodEl ? methodEl.value : 'alipay';
|
||
var account = accountEl ? String(accountEl.value || '').trim() : '';
|
||
if (amountMinor === null || amountMinor <= 0n) {
|
||
notyf('提现金额必须为最多两位小数的正数', 'danger');
|
||
return;
|
||
}
|
||
if (minAmount === null || amountMinor < minAmount) {
|
||
notyf('提现金额低于最低提现门槛', 'danger');
|
||
return;
|
||
}
|
||
if (remainingCount <= 0) {
|
||
notyf('当日提现次数已用尽', 'danger');
|
||
return;
|
||
}
|
||
if (remainingAmount === null || amountMinor > remainingAmount) {
|
||
notyf('本次提现后将超出单日提现总额上限', 'danger');
|
||
return;
|
||
}
|
||
if (!account) {
|
||
notyf('请填写收款账号信息', 'danger');
|
||
return;
|
||
}
|
||
busy = true;
|
||
notyf('正在提交提现申请…', 'load', '', 'aksrt-balance-withdraw-toast');
|
||
var source = modal.dataset.aksrtWithdrawSource === 'commission' ? 'commission' : 'balance';
|
||
api('/earnings/withdrawals', {
|
||
method: 'POST',
|
||
headers: { 'Idempotency-Key': operationKey(form) },
|
||
body: JSON.stringify({ amount: amount, method: method, account: account, source: source })
|
||
}).then(function (r) {
|
||
busy = false;
|
||
if (!r.ok || (r.json && r.json.code && !r.json.success)) {
|
||
notyf((r.json && r.json.message) || '提现失败', 'danger', 5, 'aksrt-balance-withdraw-toast');
|
||
return;
|
||
}
|
||
var data = (r.json && r.json.data) ? r.json.data : r.json;
|
||
resetOperationKey(form);
|
||
var successMessage = (data && data.message) || '申请已提交';
|
||
if (data && data.withdrawal_number) successMessage += '(' + data.withdrawal_number + ')';
|
||
notyf(successMessage, 'success', 5, 'aksrt-balance-withdraw-toast');
|
||
closeModal(modal);
|
||
// 佣金提现刷新推广收益面板,余额提现刷新余额面板;各自拉取对应片段。
|
||
var panelName = source === 'commission' ? 'revenue' : 'credits';
|
||
var panel = document.getElementById('panel-' + panelName) || (modal.closest ? modal.closest('.dashboard-panel') : null);
|
||
var rest = (window.aksrtData && (window.aksrtData.restUrl || window.aksrtData.apiUrl)) || (window.aksrtUserCenter && window.aksrtUserCenter.restUrl) || '';
|
||
if (panel && rest) {
|
||
fetch(rest.replace(/\/$/, '') + '/user-center/panel/' + panelName, { credentials: 'same-origin', headers: { 'X-WP-Nonce': (window.aksrtData && (window.aksrtData.restNonce || window.aksrtData.nonce)) || '' } })
|
||
.then(function (response) { return response.json(); })
|
||
.then(function (payload) {
|
||
if (!payload || !payload.success || !payload.data || typeof payload.data.html !== 'string') return;
|
||
panel.innerHTML = payload.data.html;
|
||
if (window.jQuery) jQuery(document).trigger('post_ajax.ed', [panel]);
|
||
if (typeof window.auto_fun === 'function') window.auto_fun();
|
||
});
|
||
}
|
||
}).catch(function () {
|
||
busy = false;
|
||
notyf('网络错误,请重试', 'danger', 5, 'aksrt-balance-withdraw-toast');
|
||
});
|
||
});
|
||
}
|
||
|
||
function initAssetTabs() {
|
||
if (document.documentElement.dataset.aksrtAssetTabsBound === 'true') return;
|
||
document.documentElement.dataset.aksrtAssetTabsBound = 'true';
|
||
|
||
function select(key) {
|
||
document.querySelectorAll('.uc-asset-tabs__nav [data-aksrt-asset-tab]').forEach(function (link) {
|
||
var li = link.closest('li');
|
||
if (!li) return;
|
||
var linkKey = link.getAttribute('data-aksrt-asset-tab');
|
||
li.classList.toggle('active', linkKey === key || (key === 'points_date' && linkKey === 'points_free'));
|
||
});
|
||
document.querySelectorAll('[data-aksrt-asset-pane]').forEach(function (pane) {
|
||
var active = pane.getAttribute('data-aksrt-asset-pane') === key;
|
||
pane.classList.toggle('is-active', active);
|
||
pane.hidden = !active;
|
||
});
|
||
}
|
||
|
||
function loadPage(key, page) {
|
||
var panel = document.querySelector('#panel-credits');
|
||
var rest = (window.aksrtData && (window.aksrtData.restUrl || window.aksrtData.apiUrl)) || '';
|
||
if (!panel || !rest) return;
|
||
panel.classList.add('is-loading');
|
||
var url = rest.replace(/\/$/, '') + '/user-center/panel/credits?asset=' + encodeURIComponent(key) + '&page=' + encodeURIComponent(page);
|
||
fetch(url, {
|
||
credentials: 'same-origin',
|
||
headers: { 'X-WP-Nonce': (window.aksrtData && (window.aksrtData.restNonce || window.aksrtData.nonce)) || '' }
|
||
}).then(function (response) {
|
||
return response.json().then(function (payload) {
|
||
if (!response.ok || !payload || !payload.success || !payload.data || typeof payload.data.html !== 'string') throw new Error('asset-page-load-failed');
|
||
panel.innerHTML = payload.data.html;
|
||
if (window.jQuery) jQuery(document).trigger('post_ajax.ed', [panel]);
|
||
if (typeof window.auto_fun === 'function') window.auto_fun();
|
||
select(key);
|
||
});
|
||
}).catch(function () {
|
||
notyf('记录加载失败,请重试', 'danger');
|
||
}).finally(function () {
|
||
panel.classList.remove('is-loading');
|
||
});
|
||
}
|
||
|
||
document.addEventListener('click', function (e) {
|
||
var pageButton = e.target.closest('[data-aksrt-asset-page]');
|
||
if (pageButton) {
|
||
e.preventDefault();
|
||
loadPage(pageButton.getAttribute('data-aksrt-asset-key') || 'points', parseInt(pageButton.getAttribute('data-aksrt-asset-page') || '1', 10) || 1);
|
||
return;
|
||
}
|
||
var link = e.target.closest('[data-aksrt-asset-tab]');
|
||
if (!link) return;
|
||
e.preventDefault();
|
||
select(link.getAttribute('data-aksrt-asset-tab'));
|
||
});
|
||
}
|
||
|
||
|
||
function initWithdrawHistory() {
|
||
var modal = document.querySelector('[data-aksrt-withdraw-history-modal]');
|
||
if (!modal || modal.dataset.aksrtWithdrawHistoryInitialized === '1') return;
|
||
modal.dataset.aksrtWithdrawHistoryInitialized = '1';
|
||
var list = modal.querySelector('[data-aksrt-withdraw-history-list]');
|
||
var pagination = modal.querySelector('[data-aksrt-withdraw-history-pagination]');
|
||
var statusLabels = {
|
||
pending: '待处理',
|
||
processing: '处理中',
|
||
completed: '已完成',
|
||
rejected: '已拒绝'
|
||
};
|
||
var currentPage = 1;
|
||
var loading = false;
|
||
|
||
function text(value) {
|
||
return value === null || value === undefined ? '' : String(value);
|
||
}
|
||
|
||
function appendText(parent, value, className) {
|
||
var node = document.createElement('span');
|
||
if (className) node.className = className;
|
||
node.textContent = text(value);
|
||
parent.appendChild(node);
|
||
return node;
|
||
}
|
||
|
||
function formatGrossAmount(value) {
|
||
return text(value).replace(/\.00$/, '');
|
||
}
|
||
|
||
function formatNetAmount(value) {
|
||
return text(value);
|
||
}
|
||
|
||
function renderItems(items) {
|
||
if (!list) return;
|
||
list.textContent = '';
|
||
if (!Array.isArray(items) || !items.length) {
|
||
var empty = document.createElement('div');
|
||
empty.className = 'uc-transactions__empty';
|
||
var emptyText = document.createElement('p');
|
||
emptyText.textContent = '暂无提现记录';
|
||
empty.appendChild(emptyText);
|
||
list.appendChild(empty);
|
||
return;
|
||
}
|
||
items.forEach(function (item) {
|
||
var status = text(item && item.status).toLowerCase();
|
||
var row = document.createElement('div');
|
||
row.className = 'uc-withdraw-history__item';
|
||
var badge = appendText(row, statusLabels[status] || status, 'badg badg-sm uc-withdraw-history__status');
|
||
badge.classList.add(status === 'rejected' ? 'c-red' : (status === 'completed' ? 'c-green' : 'c-blue'));
|
||
var gross = text(item && (item.gross_amount || item.amount));
|
||
var net = text(item && item.amount);
|
||
var amount = document.createElement('span');
|
||
amount.className = 'uc-withdraw-history__amount em12';
|
||
amount.appendChild(document.createTextNode(formatGrossAmount(gross)));
|
||
if (net !== '') {
|
||
var netText = document.createElement('span');
|
||
netText.className = 'uc-withdraw-history__net';
|
||
netText.textContent = '(实际到账:' + formatNetAmount(net) + '元)';
|
||
amount.appendChild(netText);
|
||
}
|
||
row.appendChild(amount);
|
||
var number = document.createElement('span');
|
||
number.className = 'uc-withdraw-history__number muted-2-color em09';
|
||
var numberLabel = document.createElement('span');
|
||
numberLabel.textContent = '流水号:';
|
||
number.appendChild(numberLabel);
|
||
var code = document.createElement('code');
|
||
code.textContent = text(item && item.withdrawal_number);
|
||
number.appendChild(code);
|
||
row.appendChild(number);
|
||
appendText(row, text(item && item.created_at), 'uc-withdraw-history__meta muted-2-color em09');
|
||
list.appendChild(row);
|
||
});
|
||
}
|
||
|
||
function renderPagination(page, maxPages) {
|
||
if (!pagination) return;
|
||
pagination.textContent = '';
|
||
if (maxPages <= 1) return;
|
||
function addButton(label, target, disabled, current) {
|
||
var button = document.createElement('button');
|
||
button.type = 'button';
|
||
button.className = 'uc-withdraw-history__page' + (current ? ' is-current' : '');
|
||
button.textContent = label;
|
||
button.disabled = !!disabled;
|
||
if (current) button.setAttribute('aria-current', 'page');
|
||
button.dataset.aksrtWithdrawHistoryPage = String(target);
|
||
pagination.appendChild(button);
|
||
}
|
||
addButton('上一页', page - 1, page <= 1, false);
|
||
for (var index = 1; index <= maxPages; index += 1) addButton(String(index), index, false, index === page);
|
||
addButton('下一页', page + 1, page >= maxPages, false);
|
||
}
|
||
|
||
function loadPage(page) {
|
||
if (loading || !list) return;
|
||
loading = true;
|
||
list.textContent = '加载中…';
|
||
var rest = (window.aksrtData && (window.aksrtData.restUrl || window.aksrtData.apiUrl)) || '';
|
||
if (!rest) {
|
||
list.textContent = '记录加载失败,请重试';
|
||
loading = false;
|
||
return;
|
||
}
|
||
var url = rest.replace(/\/$/, '') + '/earnings/withdrawals?page=' + encodeURIComponent(Math.max(1, page)) + '&per_page=10';
|
||
fetch(url, {
|
||
credentials: 'same-origin',
|
||
headers: { 'X-WP-Nonce': (window.aksrtData && (window.aksrtData.restNonce || window.aksrtData.nonce)) || '' }
|
||
}).then(function (response) {
|
||
return response.json().then(function (payload) {
|
||
if (!response.ok || !payload || !Array.isArray(payload.items)) throw new Error('withdraw-history-load-failed');
|
||
currentPage = Math.max(1, parseInt(payload.page, 10) || 1);
|
||
var maxPages = Math.max(1, parseInt(payload.max_pages, 10) || 1);
|
||
renderItems(payload.items);
|
||
renderPagination(currentPage, maxPages);
|
||
});
|
||
}).catch(function () {
|
||
list.textContent = '记录加载失败,请重试';
|
||
if (pagination) pagination.textContent = '';
|
||
}).finally(function () {
|
||
loading = false;
|
||
});
|
||
}
|
||
|
||
function open() {
|
||
modal.hidden = false;
|
||
document.body.classList.add('uc-modal-open');
|
||
loadPage(1);
|
||
}
|
||
function close() {
|
||
modal.hidden = true;
|
||
document.body.classList.remove('uc-modal-open');
|
||
}
|
||
document.querySelectorAll('[data-aksrt-withdraw-history-open]').forEach(function (btn) {
|
||
btn.addEventListener('click', function (e) { e.preventDefault(); open(); });
|
||
});
|
||
modal.querySelectorAll('[data-aksrt-withdraw-history-close]').forEach(function (btn) {
|
||
btn.addEventListener('click', function (e) { e.preventDefault(); close(); });
|
||
});
|
||
if (pagination) {
|
||
pagination.addEventListener('click', function (e) {
|
||
var button = e.target.closest('[data-aksrt-withdraw-history-page]');
|
||
if (!button || button.disabled) return;
|
||
e.preventDefault();
|
||
loadPage(parseInt(button.dataset.aksrtWithdrawHistoryPage || '1', 10) || 1);
|
||
});
|
||
}
|
||
}
|
||
|
||
window.AKSRTWithdrawInit = initWithdraw;
|
||
window.AKSRTAssetBalances = {
|
||
apply: applyAssetBalances,
|
||
refresh: refreshAssetBalances
|
||
};
|
||
|
||
function bootAssets() {
|
||
refreshAssetBalances();
|
||
initSigninGoto();
|
||
initAffiliateRevenue();
|
||
initVipShop();
|
||
initBalanceCharge();
|
||
initTransfer();
|
||
initPointsPay();
|
||
initWithdraw();
|
||
initWithdrawHistory();
|
||
initAssetTabs();
|
||
}
|
||
|
||
jQuery(document).off('auto_fun.aksrt-credits').on('auto_fun.aksrt-credits', function () {
|
||
bootAssets();
|
||
});
|
||
})();
|