(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) + '
云端文件
' + '
' + esc(days) + '
剩余天数
' + '
'; } 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 '
' + '' + '第 ' + page + ' / ' + pages + ' 页(共 ' + total + ' 条)' + '
'; } 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(); } })();