88c6ce8ccc
- 迁移为 frontend-web、frontend-electron、backend-web 与 docker 部署结构 - 网页端:订阅门禁二次弹窗、套餐/支付组件化、顶栏分组对齐 - 首页:最近文件与模板库布局优化,缩略图对齐,下载与删除操作 - 新增管理后台、支付与云端文件 API,更新 README 与项目规范 Co-authored-by: Cursor <cursoragent@cursor.com>
105 lines
4.1 KiB
JavaScript
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, '&')
|
|
.replace(/</g, '<')
|
|
.replace(/>/g, '>')
|
|
.replace(/"/g, '"');
|
|
}
|
|
|
|
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,
|
|
};
|
|
})();
|