(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'; 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 buildVipOrderBody(values) { var source = values || {}; var body = {}; var packageId = normalizeId(source.packageId); var genealogyId = normalizeId(source.genealogyId); var payType = optionalText(source.payType); 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; if (payType) body.payType = payType; 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 '家谱选项无效'; } if (body.payType && body.payType !== 'wechat') return '支付方式无效'; return ''; } 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) { 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) }; } 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 = Array.isArray(data) ? data.map(normalizeGenealogyOption).filter(Boolean) : []; 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] + '
'; }).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(); if (!root.location) return; if (typeof root.location.replace === 'function') root.location.replace('login.html'); else root.location.href = '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 api = root.GenealogyApi && root.GenealogyApi.defaultClient; var packages = []; var selectedPackageId = ''; var writePending = false; if (!page) return; if (shouldRedirectToLogin(api)) { redirectToLogin(api); return; } if (!packageList || !selectedText || !genealogySelect || !form || !orderList || !api.vipPackages || !api.genealogiesMine || !api.vipOrders || !api.createVipOrder) { if (status) status.textContent = '会员服务接口初始化失败,请刷新后重试'; return; } function setStatus(message) { if (status) status.textContent = message; } function renderPackages() { packageList.innerHTML = renderVipPackages(packages, selectedPackageId); } function renderOrders(orders) { orderList.innerHTML = renderVipOrders(orders); } async function loadOrders() { var orders = normalizeVipOrders(await api.vipOrders()); renderOrders(orders); return orders; } try { var initial = await Promise.all([api.vipPackages(), api.genealogiesMine(), api.vipOrders()]); packages = normalizeVipPackages(initial[0]); renderPackages(); genealogySelect.innerHTML = renderGenealogyOptions(initial[1]); renderOrders(initial[2]); setStatus(packages.length ? '请选择会员套餐' : '当前没有可用会员套餐'); } catch (error) { if (shouldRedirectToLogin(api, error)) { redirectToLogin(api); return; } packageList.innerHTML = '
会员套餐读取失败,请稍后重试
'; orderList.innerHTML = '
会员订单读取失败,请稍后重试
'; setStatus(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; refresh.disabled = true; setStatus('正在刷新订单…'); try { await loadOrders(); setStatus('会员订单已刷新'); } catch (error) { if (shouldRedirectToLogin(api, error)) { redirectToLogin(api); return; } setStatus(error.message || '会员订单刷新失败,请稍后重试'); } finally { refresh.disabled = false; } }); } form.addEventListener('submit', async function (event) { var body; var validation; var created; var refreshed; var verified; event.preventDefault(); if (writePending) return; body = buildVipOrderBody({ packageId: selectedPackageId, genealogyId: genealogySelect.value }); validation = validateVipOrderBody(body); if (validation) { setStatus(validation); return; } writePending = true; if (submit) submit.disabled = true; setStatus('正在创建会员订单…'); try { created = normalizeVipOrder(await api.createVipOrder(body)); if (!created) throw new Error('创建响应缺少有效会员订单信息'); refreshed = normalizeVipOrders(await api.vipOrders()); verified = refreshed.find(function (item) { return item.orderId === created.orderId; }); if (!verified) throw new Error('订单已提交,但重新读取未找到对应记录'); renderOrders(refreshed); setStatus('订单已创建;当前 PC 暂未开放在线支付'); } catch (error) { if (shouldRedirectToLogin(api, error)) { redirectToLogin(api); return; } setStatus(error.message || '会员订单创建失败,请稍后重试'); } finally { writePending = false; if (submit) submit.disabled = false; } }); } return { normalizeVipPackage: normalizeVipPackage, normalizeVipPackages: normalizeVipPackages, normalizeGenealogyOption: normalizeGenealogyOption, buildVipOrderBody: buildVipOrderBody, validateVipOrderBody: validateVipOrderBody, normalizeVipOrder: normalizeVipOrder, normalizeVipOrders: normalizeVipOrders, renderVipPackages: renderVipPackages, renderGenealogyOptions: renderGenealogyOptions, renderVipOrders: renderVipOrders, init: init }; });