(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();
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 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
};
});