(function (root, factory) { if (typeof module === 'object' && module.exports) { module.exports = factory(root); return; } root.VipPages = factory(root); if (root.document) { root.document.addEventListener('DOMContentLoaded', function () { root.VipPages.init(); }); } })(typeof globalThis !== 'undefined' ? globalThis : window, function (root) { 'use strict'; function confirmAction(message) { if (root.ProfileUI && typeof root.ProfileUI.confirmAction === 'function') { return root.ProfileUI.confirmAction(message); } return Promise.resolve(!root['confirm'] || root['confirm'](message)); } var PACKAGE_TYPES = { vip: '会员套餐', storage: '存储扩容' }; var DURATION_UNITS = { permanent: '永久', day: '天', month: '个月', year: '年' }; var PAY_STATUS = { '0': '待支付', '1': '已支付', '2': '已关闭', '3': '已退款' }; function escapeHtml(value) { return String(value === undefined || value === null ? '' : value) .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"') .replace(/'/g, '''); } function normalizeId(value) { var text; if (typeof value === 'number' && !Number.isSafeInteger(value)) return ''; if (value === undefined || value === null) return ''; text = String(value).trim(); return /^[1-9][0-9]*$/.test(text) ? text : ''; } function optionalText(value) { if (value === undefined || value === null) return ''; return String(value).trim(); } function moneyText(value, optional) { var text; if ((value === undefined || value === null || value === '') && optional) return ''; if (typeof value === 'number' && (!Number.isFinite(value) || value < 0)) return null; text = String(value === undefined || value === null ? '' : value).trim(); return /^(?:0|[1-9][0-9]*)(?:\.[0-9]+)?$/.test(text) ? text : null; } function validLimit(value) { return value === undefined || value === null || (Number.isSafeInteger(Number(value)) && Number(value) >= 0); } function normalizeVipPackage(item) { var source = item || {}; var packageId = normalizeId(source.packageId); var packageName = optionalText(source.packageName); var packageType = optionalText(source.packageType); var durationUnit = optionalText(source.durationUnit); var price = moneyText(source.price, false); var originalPrice = moneyText(source.originalPrice, true); var status = String(source.status === undefined || source.status === null ? '' : source.status); if (!packageId || !packageName || !Object.prototype.hasOwnProperty.call(PACKAGE_TYPES, packageType) || !Object.prototype.hasOwnProperty.call(DURATION_UNITS, durationUnit) || price === null || originalPrice === null || status !== '0' || !validLimit(source.durationValue) || !validLimit(source.genealogyLimit) || !validLimit(source.memberLimit) || !validLimit(source.storageLimitMb)) { return null; } return { packageId: packageId, packageName: packageName, packageType: packageType, packageDesc: optionalText(source.packageDesc), price: price, originalPrice: originalPrice, durationValue: source.durationValue === undefined || source.durationValue === null ? null : Number(source.durationValue), durationUnit: durationUnit, genealogyLimit: source.genealogyLimit === undefined || source.genealogyLimit === null ? null : Number(source.genealogyLimit), memberLimit: source.memberLimit === undefined || source.memberLimit === null ? null : Number(source.memberLimit), storageLimitMb: source.storageLimitMb === undefined || source.storageLimitMb === null ? null : Number(source.storageLimitMb), remark: optionalText(source.remark) }; } function normalizeVipPackages(data) { if (!Array.isArray(data)) return []; return data.map(normalizeVipPackage).filter(Boolean); } function normalizeGenealogyOption(item) { var source = item || {}; var genealogyId = normalizeId(source.genealogyId); var genealogyName = optionalText(source.genealogyName); if (!genealogyId || !genealogyName) return null; return { genealogyId: genealogyId, genealogyName: genealogyName, surname: optionalText(source.surname) }; } function normalizeGenealogyOptions(data) { if (!Array.isArray(data)) return []; return data.map(normalizeGenealogyOption).filter(Boolean); } function resolveContextGenealogyId(options, contextId) { var normalizedContextId = normalizeId(contextId); if (!normalizedContextId || !Array.isArray(options)) return ''; return options.some(function (option) { return option.genealogyId === normalizedContextId; }) ? normalizedContextId : ''; } function buildVipOrderBody(values) { var source = values || {}; var body = {}; var packageId = normalizeId(source.packageId); var genealogyId = normalizeId(source.genealogyId); if (packageId) body.packageId = packageId; else if (source.packageId !== undefined) body.invalidPackageId = true; if (genealogyId) body.genealogyId = genealogyId; else if (source.genealogyId) body.invalidGenealogyId = true; return body; } function validateVipOrderBody(body) { if (!body || !normalizeId(body.packageId) || body.invalidPackageId) return '请选择有效会员套餐'; if (body.invalidGenealogyId || (body.genealogyId !== undefined && body.genealogyId !== null && String(body.genealogyId).trim() !== '' && !normalizeId(body.genealogyId))) { return '家谱选项无效'; } return ''; } function normalizeVipCapability(value) { var source = value || {}; var methods = Array.isArray(source.paymentMethods) ? source.paymentMethods : []; var wechat = methods.find(function (item) { return item && item.paymentMethod === 'WECHAT'; }); return { enabled: source.enabled === true && !!wechat && wechat.enabled === true, disabledReason: optionalText(source.disabledReason || wechat && wechat.disabledReason) }; } function normalizeVipOrder(item) { var source = item || {}; var orderId = normalizeId(source.orderId); var packageId = normalizeId(source.packageId); var genealogyId = source.genealogyId === undefined || source.genealogyId === null ? '' : normalizeId(source.genealogyId); var orderAmount = moneyText(source.orderAmount, false); var payAmount = moneyText(source.payAmount, false); var payStatus = String(source.payStatus === undefined || source.payStatus === null ? '' : source.payStatus); var status = String(source.status === undefined || source.status === null ? '' : source.status); if (!orderId || !packageId || !optionalText(source.orderNo) || !optionalText(source.packageName) || (source.genealogyId !== undefined && source.genealogyId !== null && !genealogyId) || orderAmount === null || payAmount === null || !Object.prototype.hasOwnProperty.call(PAY_STATUS, payStatus) || ['0', '1'].indexOf(status) < 0 || (source.transactionId !== undefined && source.transactionId !== null && !normalizeId(source.transactionId))) { return null; } return { orderId: orderId, orderNo: optionalText(source.orderNo), packageId: packageId, packageName: optionalText(source.packageName), genealogyId: genealogyId, genealogyNo: optionalText(source.genealogyNo), genealogyName: optionalText(source.genealogyName), orderAmount: orderAmount, payAmount: payAmount, payType: optionalText(source.payType), payStatus: payStatus, payTime: optionalText(source.payTime), expireTime: optionalText(source.expireTime), status: status, remark: optionalText(source.remark), transactionId: source.transactionId === undefined || source.transactionId === null ? '' : normalizeId(source.transactionId), canRefreshPayment: source.canRefreshPayment === true, canClose: source.canClose === true }; } function normalizePaymentOrder(value) { var source = value || {}; var order = normalizeVipOrder(source.order); var transactionId = normalizeId(source.transactionId); var codeUrl = optionalText(source.codeUrl); if (!order || !transactionId || !codeUrl || source.tradeType !== 'NATIVE' || source.paymentMethod !== 'WECHAT') return null; return { order: order, transactionId: transactionId, codeUrl: codeUrl, expiresAt: optionalText(source.expiresAt), completed: source.completed === true }; } function normalizePaymentStatus(value) { var source = value || {}; var transactionId = normalizeId(source.transactionId); var status = optionalText(source.status); if (!transactionId || ['CREATED', 'PAYING', 'SUCCESS', 'CLOSED', 'REFUNDING', 'REFUNDED'].indexOf(status) < 0) return null; return { transactionId: transactionId, status: status, paidAt: optionalText(source.paidAt) }; } function normalizeVipOrders(data) { if (!Array.isArray(data)) return []; return data.map(normalizeVipOrder).filter(Boolean); } function renderVipPackages(data, selectedPackageId) { var items = Array.isArray(data) ? data : []; var selectedId = normalizeId(selectedPackageId); if (!items.length) return '
当前没有可用会员套餐
'; return items.map(function (item) { var duration = item.durationUnit === 'permanent' ? DURATION_UNITS[item.durationUnit] : (item.durationValue === null ? '' : item.durationValue) + DURATION_UNITS[item.durationUnit]; var limits = [ item.genealogyLimit === null ? '' : '家谱 ' + item.genealogyLimit + ' 部', item.memberLimit === null ? '' : '成员 ' + item.memberLimit + ' 人', item.storageLimitMb === null ? '' : '存储 ' + item.storageLimitMb + ' MB' ].filter(Boolean).join(' · '); return ''; }).join(''); } function renderGenealogyOptions(data) { var items = normalizeGenealogyOptions(data); return '' + items.map(function (item) { return ''; }).join(''); } function renderVipOrders(data) { var items = normalizeVipOrders(data); if (!items.length) return '
当前没有会员订单
'; return items.map(function (item) { var details = [ item.orderNo, item.genealogyName, '订单金额 ¥' + item.orderAmount, '应付 ¥' + item.payAmount, item.payType ? '支付方式 ' + item.payType : '', item.payTime ? '支付时间 ' + item.payTime : '', item.expireTime ? '有效期至 ' + item.expireTime : '', item.remark ].filter(Boolean).join(' · '); return '

' + escapeHtml(item.packageName) + '

' + escapeHtml(details) + '

' + PAY_STATUS[item.payStatus] + '' + (item.canRefreshPayment && item.transactionId ? '' : '') + (item.canClose && item.transactionId ? '' : '') + '
'; }).join(''); } function shouldRedirectToLogin(api, error) { var status = error && (error.status || error.code); return !api || !api.getToken || !api.getToken() || Number(status) === 401; } function redirectToLogin(api) { if (api && api.clearToken) api.clearToken(); root.NavigationUtil.open('login.html'); } async function init() { var page = root.document && root.document.querySelector('[data-vip-page]'); var packageList = root.document && root.document.querySelector('[data-vip-package-list]'); var selectedText = root.document && root.document.querySelector('[data-vip-selected-package]'); var genealogySelect = root.document && root.document.querySelector('[data-vip-genealogy-options]'); var form = root.document && root.document.querySelector('[data-vip-order-form]'); var submit = root.document && root.document.querySelector('[data-vip-order-submit]'); var status = root.document && root.document.querySelector('[data-vip-order-status]'); var refresh = root.document && root.document.querySelector('[data-vip-order-refresh]'); var orderList = root.document && root.document.querySelector('[data-vip-order-list]'); var paymentPanel = root.document && root.document.querySelector('[data-vip-payment-panel]'); var paymentQr = root.document && root.document.querySelector('[data-vip-payment-qr]'); var paymentText = root.document && root.document.querySelector('[data-vip-payment-text]'); var api = root.GenealogyApi && root.GenealogyApi.defaultClient; var packages = []; var selectedPackageId = ''; var contextGenealogyId = root.ProfileUI && typeof root.ProfileUI.getGenealogyId === 'function' ? root.ProfileUI.getGenealogyId() : ''; var writePending = false; var purchaseEnabled = false; if (!page) return; if (shouldRedirectToLogin(api)) { redirectToLogin(api); return; } if (!packageList || !selectedText || !genealogySelect || !form || !orderList || !api.vipCapability || !api.vipPackages || !api.genealogiesMine || !api.vipOrders || !api.createVipOrder) { setListState(packageList, 'error', '会员套餐服务初始化失败,请刷新后重试。'); setListState(orderList, 'error', '会员订单服务初始化失败,请刷新后重试。'); setStatus('error', '会员服务接口初始化失败,请刷新后重试。'); return; } function setApiState(target, type, message) { if (!target) return; if (root.ProfileUI && root.ProfileUI.setApiState && type) { root.ProfileUI.setApiState(target, type, message); return; } target.textContent = message || ''; } function setStatus(type, message) { setApiState(status, type, message); } function setListState(target, type, message) { setApiState(target, type, message); } function setPendingButton(button, pending, label) { if (!button) return; if (!button.dataset.defaultLabel) button.dataset.defaultLabel = button.textContent; button.disabled = pending; button.textContent = pending ? label : button.dataset.defaultLabel; } function renderPackages() { if (!packages.length) { setListState(packageList, 'empty', '当前没有可用会员套餐。'); return; } packageList.innerHTML = renderVipPackages(packages, selectedPackageId); } function renderOrders(orders) { var items = normalizeVipOrders(orders); if (!items.length) { setListState(orderList, 'empty', '当前没有会员订单。'); return; } orderList.innerHTML = renderVipOrders(items); } async function loadOrders() { var orders = normalizeVipOrders(await api.vipOrders()); renderOrders(orders); return orders; } function showPayment(payment) { if (!paymentPanel || !paymentQr || !paymentText) return; paymentPanel.hidden = false; paymentQr.innerHTML = ''; if (typeof root.QRCode === 'function') { new root.QRCode(paymentQr, { text: payment.codeUrl, width: 220, height: 220 }); } else { paymentQr.textContent = payment.codeUrl; } paymentText.textContent = '请使用微信扫码支付' + (payment.expiresAt ? ',支付单有效期至 ' + payment.expiresAt : ''); paymentPanel.setAttribute('data-transaction-id', payment.transactionId); } setListState(packageList, 'loading', '正在读取会员套餐…'); setListState(orderList, 'loading', '正在读取会员订单…'); setStatus('loading', '正在读取会员服务…'); try { var initial = await Promise.all([api.vipCapability(), api.vipPackages(), api.genealogiesMine(), api.vipOrders()]); var capability = normalizeVipCapability(initial[0]); var genealogyOptions = normalizeGenealogyOptions(initial[2]); var selectedContextGenealogyId = resolveContextGenealogyId(genealogyOptions, contextGenealogyId); purchaseEnabled = capability.enabled; packages = normalizeVipPackages(initial[1]); renderPackages(); genealogySelect.innerHTML = renderGenealogyOptions(genealogyOptions); if (selectedContextGenealogyId) genealogySelect.value = selectedContextGenealogyId; renderOrders(initial[3]); submit.disabled = !purchaseEnabled; setStatus(purchaseEnabled ? '' : 'forbidden', purchaseEnabled && packages.length ? (selectedContextGenealogyId ? '已关联当前家谱,请选择会员套餐' : '请选择会员套餐') : (capability.disabledReason || '当前暂不能使用微信扫码支付')); } catch (error) { if (shouldRedirectToLogin(api, error)) { redirectToLogin(api); return; } setListState(packageList, 'error', '会员套餐读取失败,请稍后重试。'); setListState(orderList, 'error', '会员订单读取失败,请稍后重试。'); setStatus('error', error.message || '会员服务读取失败,请稍后重试。'); return; } packageList.addEventListener('click', function (event) { var button = event.target.closest('[data-vip-package-id]'); var packageId; var selected; if (!button || !packageList.contains(button)) return; packageId = normalizeId(button.getAttribute('data-vip-package-id')); selected = packages.find(function (item) { return item.packageId === packageId; }); if (!selected) return; selectedPackageId = selected.packageId; selectedText.textContent = '已选择:' + selected.packageName + '(¥' + selected.price + ')'; renderPackages(); setStatus('', '可以创建会员订单'); }); if (refresh) { refresh.addEventListener('click', async function () { if (writePending) return; setPendingButton(refresh, true, '正在刷新…'); setStatus('loading', '正在刷新会员订单…'); try { await loadOrders(); setStatus('', '会员订单已刷新'); } catch (error) { if (shouldRedirectToLogin(api, error)) { redirectToLogin(api); return; } setStatus('error', error.message || '会员订单刷新失败,请稍后重试。'); } finally { setPendingButton(refresh, false, '正在刷新…'); } }); } form.addEventListener('submit', async function (event) { var body; var validation; var payment; var created; var refreshed; var verified; event.preventDefault(); if (writePending) return; if (!purchaseEnabled) return setStatus('forbidden', '当前暂不能创建支付订单'); body = buildVipOrderBody({ packageId: selectedPackageId, genealogyId: genealogySelect.value }); validation = validateVipOrderBody(body); if (validation) { setStatus('error', validation); return; } writePending = true; setPendingButton(submit, true, '正在创建订单…'); setStatus('loading', '正在创建会员订单…'); try { payment = normalizePaymentOrder(await api.createVipOrder(body)); if (!payment) throw new Error('创建响应缺少有效微信扫码支付信息'); created = payment.order; showPayment(payment); refreshed = normalizeVipOrders(await api.vipOrders()); verified = refreshed.find(function (item) { return item.orderId === created.orderId; }); if (!verified) throw new Error('订单已提交,但重新读取未找到对应记录'); renderOrders(refreshed); setStatus('', '支付单已创建,请使用微信扫码支付。'); } catch (error) { if (shouldRedirectToLogin(api, error)) { redirectToLogin(api); return; } setStatus('error', error.message || '会员订单创建失败,请稍后重试。'); } finally { writePending = false; setPendingButton(submit, false, '正在创建订单…'); } }); orderList.addEventListener('click', async function (event) { var refreshButton = event.target.closest('[data-vip-payment-refresh]'); var closeButton = event.target.closest('[data-vip-payment-close]'); var transactionId; var paymentStatus; if (writePending || !refreshButton && !closeButton) return; transactionId = (refreshButton || closeButton).getAttribute(refreshButton ? 'data-vip-payment-refresh' : 'data-vip-payment-close'); writePending = true; try { if (closeButton) { if (!await confirmAction('确认关闭这笔支付吗?')) return; await api.closeVipPayment(transactionId); setStatus('', '支付单已关闭'); } else { paymentStatus = normalizePaymentStatus(await api.vipPaymentStatus(transactionId)); if (!paymentStatus) throw new Error('支付状态响应无效'); setStatus('', paymentStatus.status === 'SUCCESS' ? '支付成功' : '当前支付状态:' + paymentStatus.status); } await loadOrders(); } catch (error) { setStatus('error', error.message || '支付状态操作失败'); } finally { writePending = false; } }); } return { normalizeVipPackage: normalizeVipPackage, normalizeVipPackages: normalizeVipPackages, normalizeGenealogyOption: normalizeGenealogyOption, normalizeGenealogyOptions: normalizeGenealogyOptions, resolveContextGenealogyId: resolveContextGenealogyId, buildVipOrderBody: buildVipOrderBody, validateVipOrderBody: validateVipOrderBody, normalizeVipCapability: normalizeVipCapability, normalizeVipOrder: normalizeVipOrder, normalizeVipOrders: normalizeVipOrders, normalizePaymentOrder: normalizePaymentOrder, normalizePaymentStatus: normalizePaymentStatus, renderVipPackages: renderVipPackages, renderGenealogyOptions: renderGenealogyOptions, renderVipOrders: renderVipOrders, init: init }; });