(function () {
'use strict';
var base = (window.SOON_DEPLOY_CONFIG && window.SOON_DEPLOY_CONFIG.api_v1_base) || '/api/v1';
var currentMembership = null;
var orderState = { page: 1, size: 8, total: 0 };
var esc = typeof soonEscapeHtml === 'function' ? soonEscapeHtml : function (s) {
if (s == null) return '';
return String(s);
};
var STATUS_LABEL = {
pending: '待支付',
paid: '已支付',
cancelled: '已取消',
refunded: '已退款',
};
var CHANNEL_LABEL = { alipay: '支付宝', wechat: '微信' };
function apiFetch(url, opts) {
if (typeof soonAuthedFetch === 'function') return soonAuthedFetch(url, opts || {});
opts = opts || {};
var token = localStorage.getItem('soon_access') || '';
opts.headers = Object.assign({}, opts.headers || {});
if (token) opts.headers.Authorization = 'Bearer ' + token;
return fetch(url, opts);
}
function parseApiJson(r) {
return r.text().then(function (text) {
try {
return JSON.parse(text);
} catch (e) {
return { ok: false, message: '服务暂时不可用,请稍后重试' };
}
});
}
function apiGet(p) {
return apiFetch(base + p).then(function (r) {
if (!r.ok && r.status === 401) return { ok: false, message: '未登录或会话已过期' };
return parseApiJson(r);
});
}
function apiPost(p, body) {
return apiFetch(base + p, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body || {}),
}).then(function (r) {
if (!r.ok && r.status === 401) return { ok: false, message: '未登录或会话已过期' };
return parseApiJson(r);
});
}
function usageMetrics(usage, quotaMb) {
var bytes = (usage && usage.storage_bytes) || 0;
var totalBytes = (quotaMb || 0) * 1024 * 1024;
var pctNum = totalBytes > 0 ? (bytes / totalBytes) * 100 : 0;
var pctLabel = pctNum > 0 && pctNum < 1 ? '<1' : String(Math.min(100, Math.round(pctNum)));
var barWidth = bytes > 0 ? Math.max(pctNum < 1 ? 0.8 : pctNum, 0.8) : 0;
return {
pctNum: pctNum,
pctLabel: pctLabel,
barWidth: Math.min(100, barWidth),
usedDisplay: (usage && usage.used_display) || '0 MB',
};
}
function showPayBanner() {
var b = document.getElementById('payBanner');
if (!b) return;
b.classList.add('is-visible');
setTimeout(function () { b.classList.remove('is-visible'); }, 5000);
}
function initLanguageSelect() {
var sel = document.getElementById('language_select');
if (!sel) return;
sel.value = localStorage.getItem('lang') || 'zh';
sel.onchange = function () { localStorage.setItem('lang', sel.value); };
}
function isPaidMember(m) {
if (!m) return false;
if (m.is_member === true) return true;
return m.tier === 'member' || m.tier === 'pro';
}
function statusPill(sub, isMember) {
if (!isMember) {
return '免费版';
}
var st = (sub && sub.status) || 'active';
var cls = 'soon-member-status-pill--active';
var text = '生效中';
if (st === 'expiring') { cls = 'soon-member-status-pill--expiring'; text = '即将到期'; }
return '' + text + '';
}
function storageRing(metrics, warn) {
var r = 52;
var c = 2 * Math.PI * r;
var offset = c - (c * Math.min(metrics.pctNum, 100) / 100);
var fillCls = warn ? ' soon-member-storage-ring__fill--warn' : '';
return '
' +
'
' +
'
' +
'' + metrics.pctLabel + '%' +
'已使用
';
}
function renderMyPlan(m) {
var el = document.getElementById('myPlan');
if (!el) return;
if (!m) {
el.innerHTML = '';
var retry = document.getElementById('myPlanRetry');
if (retry) retry.onclick = loadMembership;
return;
}
currentMembership = m;
var isMember = isPaidMember(m);
if (typeof window.soonApplyMembership === 'function') {
window.soonApplyMembership(m);
} else if (typeof window.soonLoadMembership === 'function') {
window.soonLoadMembership(true);
}
var usage = m.usage || {};
var sub = m.subscription || {};
if (!isMember) {
el.innerHTML =
'' +
'
' +
'
' +
'免费版' + statusPill(sub, false) + '
' +
'
' + esc(m.name || '免费版') + '
' +
'
' + esc(m.description || '免费体验设计与编辑') + '
' +
'
订阅后可预览、打印、保存并导出作品
' +
'
' +
'' +
'
';
var goPlans = document.getElementById('myPlanGoPlans');
if (goPlans) {
goPlans.onclick = function () {
var sec = document.getElementById('plans');
if (sec) sec.scrollIntoView({ behavior: 'smooth', block: 'start' });
};
}
return;
}
var quota = m.quota_mb || 2048;
var metrics = usageMetrics(usage, quota);
var warn = metrics.pctNum >= 90;
var barCls = warn ? 'soon-member-progress__bar soon-member-progress__bar--warn' : 'soon-member-progress__bar';
var expires = sub.expires_at
? esc(String(sub.expires_at).slice(0, 16).replace('T', ' '))
: '—';
var days = sub.days_remaining != null ? sub.days_remaining + ' 天' : '—';
el.innerHTML =
'' +
'
' +
'
' +
'
' +
'订阅版' + statusPill(sub, true) + '
' +
'
' + esc(m.name) + '
' +
'
' + esc(m.description || '已解锁完整交付能力') + '
' +
'
有效期至 ' + expires + ' · 剩余 ' + esc(days) + '
' +
storageRing(metrics, warn) + '
' +
'
' +
'
云端存储占用' +
esc(metrics.usedDisplay) + ' / ' + esc(m.quota_display || quota + ' MB') +
'(' + metrics.pctLabel + '%)
' +
'
' +
'
' +
'
' +
'
' + (usage.files_count || 0) +
'
云端文件
' +
'
' +
'
';
}
function orderBadge(status, refundStatus) {
if (refundStatus === 'pending') {
return '退款审核';
}
var cls = 'soon-member-order-badge--' + (status || 'pending');
return '' + esc(STATUS_LABEL[status] || status) + '';
}
function orderPagerHtml() {
var page = orderState.page;
var size = orderState.size;
var total = orderState.total;
var pages = Math.max(1, Math.ceil(total / size));
if (total === 0) return '';
return '';
}
function bindOrderPager() {
var prev = document.getElementById('orderPrev');
var next = document.getElementById('orderNext');
if (prev && !prev.disabled) {
prev.onclick = function () {
orderState.page = Math.max(1, orderState.page - 1);
loadOrders();
};
}
if (next && !next.disabled) {
next.onclick = function () {
var pages = Math.max(1, Math.ceil(orderState.total / orderState.size));
orderState.page = Math.min(pages, orderState.page + 1);
loadOrders();
};
}
}
function orderActionCell(o) {
var actions = [];
if (o.status === 'pending') {
actions.push('');
actions.push('');
} else if (o.status === 'paid' && (!o.refund_status || o.refund_status === 'none')) {
actions.push('');
}
if (!actions.length) return '—';
return '' + actions.join('') + '
';
}
function bindOrderActions() {
document.querySelectorAll('.soon-member-order-act[data-act]').forEach(function (btn) {
btn.onclick = function () {
var act = btn.dataset.act;
var no = btn.dataset.no;
if (act === 'pay') {
openPayModal(null, btn.dataset.plan, btn.dataset.price, {
orderNo: no,
channel: btn.dataset.channel || 'wechat',
});
return;
}
if (act === 'cancel') {
layer.confirm('确定取消该待支付订单?', { skin: 'soon-layer', title: '取消订单' }, function (idx) {
apiPost('/pay/orders/' + encodeURIComponent(no) + '/cancel', {}).then(function (r) {
layer.close(idx);
if (r.ok) {
soonToast('订单已取消', 'success');
loadOrders();
} else {
soonToast(r.message || '取消失败', 'warn');
}
});
});
return;
}
if (act === 'refund') {
layer.prompt({
skin: 'soon-layer',
title: '申请退款',
formType: 2,
value: '',
maxlength: 200,
}, function (reason, idx) {
reason = (reason || '').trim();
if (!reason) {
soonToast('请填写退款原因', 'warn');
return;
}
apiPost('/pay/orders/' + encodeURIComponent(no) + '/refund-request', { reason: reason }).then(function (r) {
layer.close(idx);
if (r.ok) {
soonToast('退款申请已提交', 'success');
loadOrders();
} else {
soonToast(r.message || '提交失败', 'warn');
}
});
});
}
};
});
}
function renderOrders(orders) {
var el = document.getElementById('orderHistory');
if (!el) return;
if (!orders || !orders.length) {
el.innerHTML = '暂无订单,订阅后将显示在这里
' +
orderPagerHtml();
bindOrderPager();
return;
}
var rows = orders.map(function (o) {
return '| ' + esc(o.order_no) + ' | ' + esc(o.plan_name) + ' | ¥' +
(o.amount_cents / 100).toFixed(2) + ' | ' + esc(CHANNEL_LABEL[o.channel] || o.channel) +
' | ' + orderBadge(o.status, o.refund_status) + ' | ' +
esc(String(o.paid_at || o.created_at || '').slice(0, 16).replace('T', ' ')) + ' | ' +
orderActionCell(o) + ' |
';
}).join('');
el.innerHTML = '| 订单号 | 方案 | 金额 | 渠道 | 状态 | 时间 | 操作 |
' +
rows + '
' + orderPagerHtml();
bindOrderPager();
bindOrderActions();
}
function loadOrders() {
apiGet('/pay/orders?page=' + orderState.page + '&size=' + orderState.size).then(function (res) {
if (!res.ok) {
renderOrders([]);
return;
}
var data = res.data || {};
orderState.total = data.total || 0;
orderState.page = data.page || orderState.page;
orderState.size = data.size || orderState.size;
renderOrders(data.items || []);
}).catch(function () {
renderOrders([]);
});
}
function renderPlans(items) {
var grid = document.getElementById('plans');
if (!grid || !window.SoonMemberPlan) return;
var paid = items.filter(function (p) { return p.code !== 'free' && p.price_cents > 0; });
var currentCode = currentMembership && isPaidMember(currentMembership) ? currentMembership.code : '';
if (!paid.length) {
grid.innerHTML = '暂无可用订阅方案
';
return;
}
grid.innerHTML = window.SoonMemberPlan.plansGridHtml(paid, {
allPlans: paid,
currentCode: currentCode,
isMember: isPaidMember(currentMembership),
mode: 'page',
});
grid.querySelectorAll('.soon-plan-card__cta[data-id]').forEach(function (btn) {
btn.onclick = function () {
openPayModal(Number(btn.dataset.id), btn.dataset.name, btn.dataset.price);
};
});
}
function renderPlansError(message, onRetry) {
var grid = document.getElementById('plans');
if (!grid) return;
grid.innerHTML = '' +
esc(message || '方案加载失败') +
'
';
var btn = document.getElementById('plansRetry');
if (btn && onRetry) btn.onclick = onRetry;
}
function loadPlans() {
apiGet('/plans').then(function (ps) {
if (!ps.ok) {
renderPlansError(ps.message || '方案加载失败', loadPlans);
return;
}
renderPlans((ps.data && ps.data.items) || []);
}).catch(function () {
renderPlansError('网络错误,请稍后重试', loadPlans);
});
}
function openPayModal(planId, planName, priceDisplay, opts) {
opts = opts || {};
var payCore = window.SoonMemberPay;
if (!payCore || !payCore.openPayModal) {
soonToast('支付模块未加载,请刷新页面', 'warn');
return;
}
payCore.openPayModal({
planId: planId,
planName: planName,
priceDisplay: priceDisplay,
orderNo: opts.orderNo,
channel: opts.channel || 'wechat',
onPaid: function () {
showPayBanner();
loadMembership();
},
});
}
function loadMembership() {
apiGet('/plans/me').then(function (my) {
if (!my.ok || !my.data) {
renderMyPlan(null);
return;
}
renderMyPlan(my.data.membership || my.data);
loadOrders();
loadPlans();
}).catch(function () {
renderMyPlan(null);
});
}
function init() {
if (!localStorage.getItem('soon_access')) {
location.href = 'login.web.html?redirect=' + encodeURIComponent('member.web.html');
return;
}
if (window.SoonPortalTopbar) {
SoonPortalTopbar.mount('#portal-topbar', { active: 'member' });
} else {
initLanguageSelect();
}
soonPortalAuth.init({ logoutReload: false });
apiGet('/auth/me').then(function (me) {
if (!me.ok) {
location.href = 'login.web.html?redirect=' + encodeURIComponent('member.web.html');
return;
}
if (me.data && me.data.role === 'admin') {
var adminEl = document.getElementById('auth_admin');
if (adminEl) adminEl.style.display = 'inline-flex';
}
loadMembership();
if (new URLSearchParams(location.search).get('paid') || sessionStorage.getItem('soon_pay_return')) {
sessionStorage.removeItem('soon_pay_return');
showPayBanner();
loadMembership();
}
}).catch(function () {
renderMyPlan(null);
renderPlansError('网络错误,请稍后重试', function () { location.reload(); });
var ordersEl = document.getElementById('orderHistory');
if (ordersEl) {
ordersEl.innerHTML = '加载失败,
';
var ordersRetry = document.getElementById('ordersRetry');
if (ordersRetry) ordersRetry.onclick = function () { location.reload(); };
}
});
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();