feat(pay): 卡密兑换入口收敛到三个购买弹窗并对齐参考主题
- 移除余额卡片独立「卡密兑换」通用弹窗,兑换入口分布到充值/积分/会员三个弹窗的支付方式中 - 充值弹窗补齐卡密 logo 与仅卡密时的金额选择隐藏逻辑 - 积分购买弹窗 1:1 复刻参考主题 points-pay modal(商品档位/自定义/支付方式/立即支付),REST 改为 product+custom 契约 - 会员弹窗注入卡密渠道:视图模型透传 card_pass,前端渲染卡密盒并直兑 /pay/card-pass/redeem type=vip - 后台卡密兑换拆分为余额/积分/会员三个独立开关,VIP 段旧会员卡密组清理 - REST type 改为必填(balance/vip/credits),三类型独立闸门 - 测试重写为 card-pass-modal-redeem 契约,321 项全部通过
This commit is contained in:
1 parent
74e43aaa76
commit
b88eab0bfe
18 files changed
+746
-472
No files matched your search
+183
-103
@@ -266,79 +266,28 @@
|
||||
});
|
||||
}
|
||||
|
||||
// ===== 卡密兑换(余额 / 会员 / 积分,通用弹窗由后端自动识别类型) =====
|
||||
function initCardPass() {
|
||||
var roots = document.querySelectorAll('[data-aksrt-card-pass]');
|
||||
if (!roots || !roots.length) return;
|
||||
|
||||
var modal = document.querySelector('[data-aksrt-card-pass-modal]');
|
||||
if (modal) {
|
||||
modal.querySelectorAll('[data-aksrt-card-pass-close]').forEach(function (btn) {
|
||||
if (btn.dataset.aksrtCardPassCloseBound === '1') return;
|
||||
btn.dataset.aksrtCardPassCloseBound = '1';
|
||||
btn.addEventListener('click', function () { closeModal(modal); });
|
||||
});
|
||||
}
|
||||
document.querySelectorAll('[data-aksrt-card-pass-open]').forEach(function (btn) {
|
||||
if (btn.dataset.aksrtCardPassOpenBound === '1') return;
|
||||
btn.dataset.aksrtCardPassOpenBound = '1';
|
||||
btn.addEventListener('click', function () { openModal(modal); });
|
||||
});
|
||||
|
||||
Array.prototype.forEach.call(roots, function (root) {
|
||||
var busy = false;
|
||||
resetOperationKeyOnChange(root);
|
||||
|
||||
var btn = root.querySelector('[data-aksrt-card-redeem]');
|
||||
if (!btn) return;
|
||||
|
||||
btn.addEventListener('click', function () {
|
||||
if (busy || btn.disabled) return;
|
||||
var type = btn.getAttribute('data-type') || root.getAttribute('data-aksrt-card-pass') || '';
|
||||
var pwInput = root.querySelector('[data-aksrt-card-password]');
|
||||
var cardInput = root.querySelector('[data-aksrt-card-no]');
|
||||
var password = pwInput ? String(pwInput.value || '').trim() : '';
|
||||
var card = cardInput ? String(cardInput.value || '').trim() : '';
|
||||
// 卡号为选填:是否需要卡号由后端按卡密类型判定并返回错误提示。
|
||||
if (!password) {
|
||||
notyf('请输入卡密', 'danger');
|
||||
if (pwInput) pwInput.focus();
|
||||
return;
|
||||
}
|
||||
|
||||
busy = true;
|
||||
btn.disabled = true;
|
||||
notyf('正在兑换…', 'load', '', 'aksrt-card-pass-toast');
|
||||
|
||||
var body = { password: password, type: type };
|
||||
if (card) body.card = card;
|
||||
|
||||
api('/pay/card-pass/redeem', {
|
||||
method: 'POST',
|
||||
headers: { 'Idempotency-Key': operationKey(root) },
|
||||
body: JSON.stringify(body)
|
||||
}).then(function (r) {
|
||||
busy = false;
|
||||
btn.disabled = false;
|
||||
if (!r.ok || (r.json && r.json.code && !r.json.success)) {
|
||||
notyf((r.json && r.json.message) || '兑换失败', 'danger', 5, 'aksrt-card-pass-toast');
|
||||
return;
|
||||
}
|
||||
var data = (r.json && r.json.data) ? r.json.data : r.json;
|
||||
resetOperationKey(root);
|
||||
// 通用兑换(type 为空)时以后端识别出的卡密类型为准。
|
||||
var redeemedType = (data && data.type) || type;
|
||||
if (redeemedType === 'vip' && window.AksrtModalRuntime && typeof window.AksrtModalRuntime.invalidate === 'function') {
|
||||
window.AksrtModalRuntime.invalidate(['vip', 'orders']);
|
||||
}
|
||||
notyf((data && data.message) || '兑换成功,正在刷新…', 'success', 5, 'aksrt-card-pass-toast');
|
||||
window.setTimeout(function () { window.location.reload(); }, 800);
|
||||
}).catch(function () {
|
||||
busy = false;
|
||||
btn.disabled = false;
|
||||
notyf('网络错误,请重试', 'danger', 5, 'aksrt-card-pass-toast');
|
||||
});
|
||||
});
|
||||
// ===== 卡密兑换(三个购买弹窗共用的卡密渠道提交) =====
|
||||
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: '网络错误,请重试' };
|
||||
});
|
||||
}
|
||||
|
||||
@@ -471,13 +420,24 @@
|
||||
}
|
||||
});
|
||||
}
|
||||
// 支付方式(.payment-method-radio)
|
||||
// 支付方式(.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) {
|
||||
@@ -485,11 +445,36 @@
|
||||
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"]');
|
||||
@@ -686,40 +671,136 @@
|
||||
});
|
||||
}
|
||||
|
||||
function initPointsExchange() {
|
||||
var modal = document.querySelector('[data-aksrt-points-exchange-modal]');
|
||||
if (!modal || modal.dataset.aksrtPointsExchangeInitialized === '1') return;
|
||||
modal.dataset.aksrtPointsExchangeInitialized = '1';
|
||||
var form = modal.querySelector('[data-aksrt-points-exchange-form]');
|
||||
var amountEl = form ? form.querySelector('[data-aksrt-points-exchange-amount]') : null;
|
||||
var preview = form ? form.querySelector('[data-aksrt-points-exchange-preview]') : null;
|
||||
// ===== 购买积分(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;
|
||||
document.querySelectorAll('[data-aksrt-points-exchange-open]').forEach(function (btn) {
|
||||
btn.addEventListener('click', function () { openModal(modal); });
|
||||
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.querySelectorAll('[data-aksrt-points-exchange-close]').forEach(function (btn) {
|
||||
btn.addEventListener('click', function () { closeModal(modal); });
|
||||
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);
|
||||
function updatePreview() {
|
||||
if (!preview || !amountEl) return;
|
||||
var amount = balanceMinor(amountEl.value);
|
||||
var rateRaw = amountEl.getAttribute('data-aksrt-points-exchange-rate') || '';
|
||||
var rate = /^[1-9]\d*$/.test(rateRaw) ? BigInt(rateRaw) : 0n;
|
||||
var scaled = amount !== null ? amount * rate : 0n;
|
||||
preview.textContent = amount !== null && amount > 0n && rate > 0n && scaled % 100n === 0n
|
||||
? '预计获得 ' + String(scaled / 100n) + ' 积分'
|
||||
: '';
|
||||
}
|
||||
amountEl.addEventListener('input', updatePreview);
|
||||
updatePreview();
|
||||
form.addEventListener('submit', function (e) {
|
||||
e.preventDefault();
|
||||
if (busy) return;
|
||||
var amount = amountEl ? String(amountEl.value || '').trim() : '';
|
||||
if (!/^(0|[1-9]\d*)(?:\.\d{1,2})?$/.test(amount) || /^0(?:\.0{1,2})?$/.test(amount)) {
|
||||
notyf('请输入最多两位小数的正数余额', 'danger');
|
||||
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;
|
||||
@@ -727,7 +808,7 @@
|
||||
api('/pay/balance/points-exchange', {
|
||||
method: 'POST',
|
||||
headers: { 'Idempotency-Key': operationKey(form) },
|
||||
body: JSON.stringify({ amount: amount })
|
||||
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)) {
|
||||
@@ -1054,10 +1135,9 @@
|
||||
initAffiliateRevenue();
|
||||
initVipShop();
|
||||
initVipPointsExchange();
|
||||
initCardPass();
|
||||
initBalanceCharge();
|
||||
initTransfer();
|
||||
initPointsExchange();
|
||||
initPointsPay();
|
||||
initWithdraw();
|
||||
initWithdrawHistory();
|
||||
initAssetTabs();
|
||||
|
||||
+112
-16
@@ -102,7 +102,7 @@
|
||||
append(parent, wrap);
|
||||
}
|
||||
|
||||
function renderMethods(parent, methods) {
|
||||
function renderMethods(parent, methods, cardPass) {
|
||||
methods = Array.isArray(methods) ? methods : [];
|
||||
if (!methods.length) {
|
||||
append(parent, node("div", "muted-2-color text-center padding-10", "暂无可用支付方式"));
|
||||
@@ -115,23 +115,60 @@
|
||||
one.value = text(methods[0].id || "balance");
|
||||
one.setAttribute("data-aksrt-vip-method-fixed", "");
|
||||
append(parent, one);
|
||||
return;
|
||||
} else {
|
||||
var wrap = node("div", "uc-vip-shop__methods mb10");
|
||||
wrap.setAttribute("data-aksrt-vip-methods", "");
|
||||
append(wrap, node("span", "uc-vip-shop__methods-label", "支付方式"));
|
||||
methods.forEach(function (method, index) {
|
||||
var label = node("label", "uc-vip-shop__method");
|
||||
var input = node("input");
|
||||
input.type = "radio";
|
||||
input.name = "aksrt_vip_pay_method";
|
||||
input.value = text(method.id || "balance");
|
||||
input.checked = index === 0;
|
||||
append(label, input);
|
||||
append(label, node("span", "", method.name || method.id || ""));
|
||||
append(wrap, label);
|
||||
});
|
||||
append(parent, wrap);
|
||||
}
|
||||
var wrap = node("div", "uc-vip-shop__methods mb10");
|
||||
wrap.setAttribute("data-aksrt-vip-methods", "");
|
||||
append(wrap, node("span", "uc-vip-shop__methods-label", "支付方式"));
|
||||
methods.forEach(function (method, index) {
|
||||
var label = node("label", "uc-vip-shop__method");
|
||||
var input = node("input");
|
||||
input.type = "radio";
|
||||
input.name = "aksrt_vip_pay_method";
|
||||
input.value = text(method.id || "balance");
|
||||
input.checked = index === 0;
|
||||
append(label, input);
|
||||
append(label, node("span", "", method.name || method.id || ""));
|
||||
append(wrap, label);
|
||||
// 卡密渠道输入盒(契约与参考主题一致:data-controller="payment_method")。
|
||||
if (cardPass) {
|
||||
var box = node("div", "uc-vip-shop__card-pass mb10");
|
||||
box.setAttribute("data-controller", "payment_method");
|
||||
box.setAttribute("data-condition", "==");
|
||||
box.setAttribute("data-value", "card_pass");
|
||||
box.style.display = "none";
|
||||
if (cardPass.desc) append(box, node("div", "muted-box muted-2-color padding-10 mb10 em09", cardPass.desc));
|
||||
append(box, node("div", "muted-2-color em09 mb6", cardPass.only_password ? "请输入卡密" : "请输入卡号和密码"));
|
||||
if (!cardPass.only_password) {
|
||||
var cardInput = node("input", "form-control mb6");
|
||||
cardInput.type = "text";
|
||||
cardInput.name = "card_pass[card]";
|
||||
cardInput.placeholder = "卡号";
|
||||
cardInput.autocomplete = "off";
|
||||
append(box, cardInput);
|
||||
}
|
||||
var pwInput = node("input", "form-control");
|
||||
pwInput.type = "text";
|
||||
pwInput.name = "card_pass[password]";
|
||||
pwInput.placeholder = "卡密";
|
||||
pwInput.autocomplete = "off";
|
||||
append(box, pwInput);
|
||||
append(parent, box);
|
||||
}
|
||||
}
|
||||
|
||||
// 卡密渠道显隐:选中 card_pass 时隐藏套餐(.charge-box)、显示卡密输入盒。
|
||||
function applyVipMethodVisibility(root) {
|
||||
var pane = root.closest ? root.closest(".aksrt-vip-pane") : null;
|
||||
pane = pane || root;
|
||||
var value = selectedMethod(pane);
|
||||
var cardBox = pane.querySelector('[data-controller="payment_method"][data-value="card_pass"]');
|
||||
if (cardBox) cardBox.style.display = value === "card_pass" ? "" : "none";
|
||||
pane.querySelectorAll(".charge-box").forEach(function (el) {
|
||||
el.style.display = value === "card_pass" ? "none" : "";
|
||||
});
|
||||
append(parent, wrap);
|
||||
}
|
||||
|
||||
function renderPointsModal(body, view) {
|
||||
@@ -248,6 +285,7 @@
|
||||
var levels = levelMap(view.levels);
|
||||
var groups = Array.isArray(catalog.level_packages) ? catalog.level_packages : [];
|
||||
var methods = Array.isArray(view.payment_methods) ? view.payment_methods : [];
|
||||
var cardPass = view.card_pass && view.card_pass.enabled ? view.card_pass : null;
|
||||
var allowed = view.allowed_actions || {};
|
||||
var reasons = view.action_reasons || {};
|
||||
var modal = node("div", "box-body payvip-modal");
|
||||
@@ -354,6 +392,11 @@
|
||||
append(pointsWrap, pointsLink);
|
||||
append(modal, pointsWrap);
|
||||
}
|
||||
|
||||
// 卡密渠道初始显隐(选中 card_pass 时隐藏套餐、显示卡密输入盒)。
|
||||
modal.querySelectorAll(".aksrt-vip-pane").forEach(function (pane) {
|
||||
applyVipMethodVisibility(pane);
|
||||
});
|
||||
append(body, modal);
|
||||
}
|
||||
|
||||
@@ -444,9 +487,62 @@
|
||||
});
|
||||
}
|
||||
|
||||
// 卡密兑换会员(type=vip):直兑,不创建支付订单。
|
||||
function redeemVipCardPass(pane, button) {
|
||||
var pwEl = pane.querySelector('input[name="card_pass[password]"]');
|
||||
var cardEl = pane.querySelector('input[name="card_pass[card]"]');
|
||||
var password = pwEl ? String(pwEl.value || "").trim() : "";
|
||||
if (!password) {
|
||||
notyf("请输入卡密", "danger");
|
||||
if (pwEl) pwEl.focus();
|
||||
return;
|
||||
}
|
||||
if (!button || button.disabled) return;
|
||||
var request = window.AksrtRequest;
|
||||
if (!request || typeof request.request !== "function") {
|
||||
notyf("会员兑换暂不可用", "danger");
|
||||
return;
|
||||
}
|
||||
button.disabled = true;
|
||||
button.classList.add("is-loading");
|
||||
notyf("正在兑换会员…", "load", "", "aksrt-vip-purchase-toast");
|
||||
var key = typeof request.createIdempotencyKey === "function" ? request.createIdempotencyKey() : "";
|
||||
request.request("/pay/card-pass/redeem", {
|
||||
method: "POST",
|
||||
headers: key ? { "Idempotency-Key": key } : {},
|
||||
body: JSON.stringify({
|
||||
type: "vip",
|
||||
card: cardEl ? String(cardEl.value || "").trim() : "",
|
||||
password: password,
|
||||
}),
|
||||
}).then(function (response) {
|
||||
button.disabled = false;
|
||||
button.classList.remove("is-loading");
|
||||
if (!response.ok || (response.json && response.json.code && !response.json.success)) {
|
||||
notyf((response.json && response.json.message) || "兑换失败", "danger", 5, "aksrt-vip-purchase-toast");
|
||||
return;
|
||||
}
|
||||
var data = response.data || (response.json && response.json.data) || {};
|
||||
if (window.AksrtModalRuntime && typeof window.AksrtModalRuntime.invalidate === "function") {
|
||||
window.AksrtModalRuntime.invalidate(["vip", "orders"]);
|
||||
}
|
||||
notyf(data.message || "兑换成功,正在刷新…", "success", 5, "aksrt-vip-purchase-toast");
|
||||
window.setTimeout(function () { window.location.reload(); }, 800);
|
||||
}).catch(function () {
|
||||
button.disabled = false;
|
||||
button.classList.remove("is-loading");
|
||||
notyf("网络错误,请重试", "danger", 5, "aksrt-vip-purchase-toast");
|
||||
});
|
||||
}
|
||||
|
||||
function bindModalUi(root) {
|
||||
if (!root || root.dataset.aksrtVipBound === "1") return;
|
||||
root.dataset.aksrtVipBound = "1";
|
||||
root.addEventListener("change", function (event) {
|
||||
var input = event.target.closest ? event.target.closest('input[name="aksrt_vip_pay_method"]') : null;
|
||||
if (!input) return;
|
||||
applyVipMethodVisibility(input);
|
||||
});
|
||||
root.addEventListener("click", function (event) {
|
||||
var tab = event.target.closest("[data-vip-tab]");
|
||||
if (tab) {
|
||||
|
||||
Reference in new issue
Block a user