Files
jiapu/public/js/vip-pages.js
T
2026-07-09 17:30:10 +08:00

271 lines
7.7 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
(function (root, factory) {
// 会员服务模块同时支持浏览器页面和 Node 单元测试。
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 documentRef = root.document;
function normalizeList(data) {
// VIP 列表接口使用通用 ListResult,兼容 rows、records、list、data 等常见包裹。
if (Array.isArray(data)) return data;
if (!data || typeof data !== 'object') return [];
if (Array.isArray(data.rows)) return data.rows;
if (Array.isArray(data.records)) return data.records;
if (Array.isArray(data.list)) return data.list;
if (Array.isArray(data.data)) return data.data;
return [];
}
function pick(item, keys, fallback) {
var source = item || {};
var index;
var value;
for (index = 0; index < keys.length; index += 1) {
value = source[keys[index]];
if (value !== undefined && value !== null && value !== '') return value;
}
return fallback || '';
}
function escapeHtml(value) {
// 套餐名、说明和订单号进入 innerHTML 前统一转义。
return String(value === undefined || value === null ? '' : value)
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#39;');
}
function getApi() {
return root.GenealogyApi && root.GenealogyApi.defaultClient;
}
function query(selector, rootNode) {
return (rootNode || documentRef).querySelector(selector);
}
function queryAll(selector, rootNode) {
return Array.prototype.slice.call((rootNode || documentRef).querySelectorAll(selector));
}
function showMessage(message) {
if (root.layui && root.layui.layer) {
root.layui.layer.msg(message);
return;
}
root.alert(message);
}
function trimOrUndefined(value) {
var text = String(value === undefined || value === null ? '' : value).trim();
return text || undefined;
}
function toNumberOrUndefined(value) {
var text = String(value === undefined || value === null ? '' : value).trim();
var number = Number(text);
return text && Number.isFinite(number) ? number : undefined;
}
function formatPrice(value) {
var number = Number(value);
if (!Number.isFinite(number)) return '价格待定';
return '¥' + number;
}
function formatDuration(value) {
var duration = String(value === undefined || value === null ? '' : value).trim();
return duration ? duration + '天' : '有效期见套餐说明';
}
function formatPayType(value) {
var map = {
wechat: '微信支付',
alipay: '支付宝',
balance: '余额支付'
};
var key = String(value || '').trim();
return map[key] || key || '支付方式待确认';
}
function buildVipOrderBody(values) {
// VipOrderBody 来自 APP.openapi.jsonpackageId 必填,genealogyId 和 payType 可选。
var source = values || {};
return {
packageId: toNumberOrUndefined(source.packageId),
genealogyId: toNumberOrUndefined(source.genealogyId),
payType: trimOrUndefined(source.payType)
};
}
function getPackageId(item) {
return pick(item, ['packageId', 'vipPackageId', 'id'], '');
}
function buildPackageCard(item) {
var packageId = getPackageId(item);
var name = pick(item, ['packageName', 'name', 'title'], '会员套餐');
var price = pick(item, ['price', 'salePrice', 'amount'], '');
var duration = pick(item, ['durationDays', 'validDays', 'days'], '');
var description = pick(item, ['description', 'packageDesc', 'remark'], '开通后享受会员服务权益');
return '<button class="vip-package-card" type="button" data-vip-package-id="' + escapeHtml(packageId) + '">' +
'<span class="vip-package-name">' + escapeHtml(name) + '</span>' +
'<strong>' + escapeHtml(formatPrice(price)) + '</strong>' +
'<small>' + escapeHtml(formatDuration(duration)) + '</small>' +
'<p>' + escapeHtml(description) + '</p></button>';
}
function buildOrderRow(item) {
var orderNo = pick(item, ['orderNo', 'orderSn', 'id'], '订单号待返回');
var packageName = pick(item, ['packageName', 'vipPackageName', 'name'], 'VIP 订单');
var payType = formatPayType(pick(item, ['payType', 'paymentType'], ''));
var status = pick(item, ['orderStatus', 'status', 'payStatus'], '状态待确认');
return '<div class="module-row vip-order-row"><div><h3>' + escapeHtml(packageName) + '</h3><p>' +
escapeHtml(orderNo) + ' · ' + escapeHtml(payType) + ' · ' + escapeHtml(status) +
'</p></div><span class="pill">订单</span></div>';
}
function renderPackages(items) {
var container = query('[data-vip-package-list]');
var list = normalizeList(items);
if (!container) return;
if (!list.length) {
container.innerHTML = '<div class="api-empty">暂无会员套餐</div>';
return;
}
container.innerHTML = list.map(buildPackageCard).join('');
}
function renderOrders(items) {
var container = query('[data-vip-order-list]');
var list = normalizeList(items);
if (!container) return;
if (!list.length) {
container.innerHTML = '<div class="api-empty">暂无会员订单</div>';
return;
}
container.innerHTML = list.map(buildOrderRow).join('');
}
function getFormValues(form) {
var values = {};
queryAll('[name]', form).forEach(function (field) {
values[field.name] = field.value;
});
return values;
}
function setOrderStatus(message) {
var status = query('[data-vip-order-status]');
if (status) status.textContent = message;
}
async function loadVipData() {
var api = getApi();
if (!api) return;
try {
renderPackages(await api.vipPackages());
renderOrders(await api.vipOrders());
} catch (error) {
showMessage(error.message || '会员服务加载失败');
}
}
function selectPackage(button) {
var packageId = button.getAttribute('data-vip-package-id') || '';
var field = query('[name="packageId"]');
queryAll('[data-vip-package-id]').forEach(function (item) {
item.classList.remove('is-selected');
});
button.classList.add('is-selected');
if (field) field.value = packageId;
}
async function submitOrder(form) {
var api = getApi();
var body = buildVipOrderBody(getFormValues(form));
if (!api) return;
if (!body.packageId) {
showMessage('请选择会员套餐');
return;
}
try {
await api.createVipOrder(body);
setOrderStatus('订单已创建,请继续完成支付');
showMessage('会员订单已创建');
renderOrders(await api.vipOrders());
} catch (error) {
showMessage(error.message || '会员订单创建失败');
}
}
function bindActions() {
if (!documentRef) return;
documentRef.addEventListener('click', function (event) {
var packageButton = event.target.closest('[data-vip-package-id]');
if (!packageButton) return;
selectPackage(packageButton);
});
documentRef.addEventListener('submit', function (event) {
var form = event.target.closest('[data-vip-order-form]');
if (!form) return;
event.preventDefault();
submitOrder(form);
});
}
function init() {
if (!documentRef || !query('[data-vip-page]')) return;
bindActions();
loadVipData();
}
return {
normalizeList: normalizeList,
buildVipOrderBody: buildVipOrderBody,
buildPackageCard: buildPackageCard,
buildOrderRow: buildOrderRow,
init: init
};
});