Files
SoonDesign/frontend-web/js/common/member-plan-core.js
T
24kycj 88c6ce8ccc 重构 monorepo 并完善网页端订阅与首页体验
- 迁移为 frontend-web、frontend-electron、backend-web 与 docker 部署结构
- 网页端:订阅门禁二次弹窗、套餐/支付组件化、顶栏分组对齐
- 首页:最近文件与模板库布局优化,缩略图对齐,下载与删除操作
- 新增管理后台、支付与云端文件 API,更新 README 与项目规范

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-08 18:17:39 +08:00

105 lines
4.1 KiB
JavaScript

(function () {
'use strict';
var CHECK_SVG = '<svg viewBox="0 0 12 12" fill="none" stroke-width="2" stroke="currentColor">' +
'<path d="M2 6l3 3 5-6"/></svg>';
function esc(s) {
if (window.SoonMemberPay && window.SoonMemberPay.esc) return window.SoonMemberPay.esc(s);
if (s == null) return '';
return String(s)
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;');
}
function isPlanFeatureLine(line) {
if (!line) return false;
return line.indexOf('到期') < 0
&& line.indexOf('订阅有效期') < 0
&& line.indexOf('订阅周期') < 0
&& line.indexOf('权益一致') < 0;
}
function featureItem(text) {
return '<li><span class="soon-plan-card__check">' + CHECK_SVG + '</span><span>' + esc(text) + '</span></li>';
}
function billingSavePercent(items, plan) {
if (window.SoonMemberPay && window.SoonMemberPay.billingSavePercent) {
return window.SoonMemberPay.billingSavePercent(items, plan);
}
return null;
}
function planCardHtml(plan, ctx) {
ctx = ctx || {};
var items = ctx.allPlans || [];
var mode = ctx.mode || 'page';
var isMember = !!ctx.isMember;
var currentCode = ctx.currentCode || '';
var selectedId = ctx.selectedId;
var feat = plan.is_recommended ? ' soon-plan-card--featured' : '';
var cur = plan.code === currentCode ? ' soon-plan-card--current' : '';
var isGate = mode === 'gate';
var isPicker = mode === 'picker' || isGate;
var picker = isPicker ? ' soon-plan-card--picker' : '';
if (isGate) picker += ' soon-plan-card--gate';
var selected = isPicker && plan.id === selectedId ? ' soon-plan-card--selected' : '';
var ribbon = plan.is_recommended
? '<span class="soon-plan-card__ribbon soon-plan-card__ribbon--rec">推荐</span>'
: (plan.code === currentCode ? '<span class="soon-plan-card__ribbon soon-plan-card__ribbon--current">当前</span>' : '');
var savePct = billingSavePercent(items, plan);
if (savePct) ribbon += '<span class="soon-plan-card__save">省 ' + savePct + '%</span>';
var badgeCls = ribbon ? ' soon-plan-card--badged' : '';
if (savePct) badgeCls += ' soon-plan-card--save';
var period = plan.duration_days > 0
? '<span class="soon-plan-card__period">/' + esc(plan.period_label || plan.duration_days + '天') + '</span>'
: '';
var features = (plan.features || []).filter(isPlanFeatureLine).map(featureItem).join('');
var desc = plan.description || '';
var price = esc(plan.price_display || (plan.price_cents / 100).toFixed(2));
var inner =
ribbon +
'<div class="soon-plan-card__head">' +
'<div class="soon-plan-card__name">' + esc(plan.name) + '</div>' +
(desc ? '<p class="soon-plan-card__desc">' + esc(desc) + '</p>' : '') +
'<div class="soon-plan-card__price-row">' +
'<span class="soon-plan-card__currency">¥</span>' +
'<span class="soon-plan-card__price">' + price + '</span>' + period + '</div></div>';
if (!isGate) {
inner += '<div class="soon-plan-card__body">' +
'<ul class="soon-plan-card__features">' + features + '</ul></div>';
}
if (isPicker) {
return '<button type="button" class="soon-plan-card' + feat + cur + badgeCls + picker + selected + '" ' +
'data-action="pick-plan" data-id="' + plan.id + '" aria-pressed="' + (plan.id === selectedId ? 'true' : 'false') + '">' +
inner + '</button>';
}
var cta = '<button type="button" class="soon-btn soon-plan-card__cta" data-id="' + plan.id +
'" data-name="' + esc(plan.name) + '" data-price="' + price + '">' +
(isMember ? '续订' : '立即订阅') + '</button>';
return '<article class="soon-plan-card' + feat + cur + badgeCls + '">' + inner +
'<div class="soon-plan-card__foot">' + cta + '</div></article>';
}
function plansGridHtml(plans, ctx) {
return (plans || []).map(function (p) {
return planCardHtml(p, ctx);
}).join('');
}
window.SoonMemberPlan = {
isPlanFeatureLine: isPlanFeatureLine,
planCardHtml: planCardHtml,
plansGridHtml: plansGridHtml,
};
})();