alphax/static/subscription.html
2026-05-14 17:56:33 +08:00

260 lines
12 KiB
HTML
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.

{% extends "base.html" %}
{% block title %}订阅中心 — AlphaX Agent Crypto{% endblock %}
{% block nav_links %}
<a class="sidebar-link" href="/app"><svg class="link-icon"><use href="#svg-dashboard"/></svg>看板</a>
<a class="sidebar-link" href="/sentiment"><svg class="link-icon"><use href="#svg-sentiment"/></svg>舆情</a>
<a class="sidebar-link active" href="/subscription"><svg class="link-icon"><use href="#svg-subscribe"/></svg>订阅</a>
<a class="sidebar-link" href="/referral"><svg class="link-icon"><use href="#svg-referral"/></svg>推荐</a>
<div class="sidebar-section-label admin-link" style="display:none">研发</div>
<a class="sidebar-link admin-link" href="/pipeline" style="display:none"><svg class="link-icon"><use href="#svg-pipeline"/></svg>链路日志</a>
<a class="sidebar-link admin-link" href="/cron" style="display:none"><svg class="link-icon"><use href="#svg-cron"/></svg>调度中心</a>
<a class="sidebar-link admin-link" href="/llm-insights" style="display:none"><svg class="link-icon"><use href="#svg-ai"/></svg>AI 记录</a>
<a class="sidebar-link admin-link" href="/strategy" style="display:none"><svg class="link-icon"><use href="#svg-target"/></svg>策略</a>
<a class="sidebar-link admin-link" href="/iteration" style="display:none"><svg class="link-icon"><use href="#svg-iterate"/></svg>迭代</a>
<a class="sidebar-link admin-link" href="/admin.html" style="display:none"><svg class="link-icon"><use href="#svg-admin"/></svg>管理</a>
{% endblock %}
{% block extra_head_css %}
<style>
.shell { position: relative; z-index: 1; width: min(100% - 64px, 900px); margin: 0 auto; padding: 48px 0 64px; }
.shell h1 { font-size: 32px; font-weight: 500; letter-spacing: -1px; margin-bottom: 8px; }
.shell .sub { color: var(--slate); font-size: 15px; margin-bottom: 32px; }
/* Status bar */
.status-bar {
border: 1px solid var(--hairline-soft); background: var(--canvas);
border-radius: var(--radius-lg); padding: 16px 20px; margin-bottom: 32px;
display: flex; align-items: center; gap: 24px; flex-wrap: wrap;
}
.status-bar .status-item { font-size: 13px; color: var(--slate); }
.status-bar .status-item strong { color: var(--ink); }
.guide-box {
border: 1px solid rgba(66,98,255,.16);
background: linear-gradient(135deg, rgba(66,98,255,.08), rgba(255,208,47,.16));
border-radius: var(--radius-xl); padding: 18px 20px; margin-bottom: 22px;
display: none;
}
.guide-box.show { display: block; }
.guide-title { font-size: 16px; font-weight: 700; color: var(--ink); margin-bottom: 6px; }
.guide-text { font-size: 14px; color: var(--slate); line-height: 1.7; }
/* Plans grid */
.plans { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px; }
.plan {
border-radius: var(--radius-xl); padding: 26px; border: 1px solid var(--hairline);
background: var(--canvas); display: flex; flex-direction: column; gap: 16px;
transition: transform .15s, border-color .15s, box-shadow .15s;
min-height: 230px;
}
.plan:hover { transform: translateY(-2px); border-color: var(--hairline-strong); box-shadow: 0 8px 22px rgba(5,0,56,.05); }
.plan.featured { border: 2px solid var(--blue); }
.plan-top { display: flex; justify-content: space-between; align-items: flex-start; gap: 12px; }
.plan h3 { font-size: 20px; font-weight: 600; letter-spacing: -.3px; }
.plan .price { font-size: 34px; font-weight: 700; line-height: 1.15; margin-top: 2px; }
.plan .price unit { font-size: 14px; font-weight: 500; color: var(--slate); }
.price-pending { display: inline-flex; align-items: center; min-height: 39px; color: var(--stone); font-size: 17px; font-weight: 600; letter-spacing: -.2px; }
.plan .desc { font-size: 13px; color: var(--slate); line-height: 1.5; min-height: 40px; }
.plan-spacer { flex: 1; }
/* Plan button */
.btn-plan {
display: flex; align-items: center; justify-content: center;
width: 100%; height: 44px; border: 0; border-radius: var(--radius-full);
font-weight: 500; font-size: 14px; cursor: pointer;
transition: background .15s, transform .15s;
}
.btn-plan.green { background: var(--green); color: #fff; }
.btn-plan.green:hover { opacity: .9; }
.btn-plan.disabled { background: var(--hairline-soft); color: var(--muted); cursor: not-allowed; }
.badge { display: inline-flex; align-items: center; font-size: 12px; font-weight: 600; padding: 4px 10px; border-radius: var(--radius-full); }
.badge.yellow { background: var(--yellow); color: var(--primary); }
.badge.gray { background: var(--surface); color: var(--stone); }
.badge.blue { background: rgba(66,98,255,.1); color: var(--blue); }
.msg { font-size: 13px; min-height: 20px; margin-top: 8px; }
.msg.ok { color: var(--green); } .msg.err { color: #e53e3e; }
@media (max-width: 700px) {
.plans { grid-template-columns: 1fr; }
.shell { width: min(100% - 32px, 900px); padding: 24px 0 48px; }
.status-bar { flex-direction: column; gap: 8px; align-items: flex-start; }
.shell h1 { font-size: 24px; }
}
</style>
{% endblock %}
{% block content %}
<div class="shell">
<h1>订阅中心</h1>
<p class="sub">新用户可免费体验 1 个月,后续按需选择方案。</p>
<div id="guideBox" class="guide-box">
<div class="guide-title" id="guideTitle">先开通套餐,开始使用 AlphaX Agent Crypto</div>
<div class="guide-text" id="guideText">新用户可领取 30 天免费体验。开通后即可进入看板、策略、迭代和舆情页面。</div>
</div>
<div class="status-bar" id="statusBar" style="display:none">
<div class="status-item">当前方案: <strong id="mePlan">--</strong></div>
<div class="status-item">到期时间: <strong id="meEnd">--</strong></div>
</div>
<div class="plans">
<div class="plan featured">
<div class="plan-top"><h3>免费体验</h3><span class="badge yellow">推荐</span></div>
<div class="price"><unit>$</unit>0</div>
<p class="desc">新用户可领取 30 天 Beta 体验。</p>
<div id="trialMsg" class="msg"></div>
<div class="plan-spacer"></div>
<button class="btn-plan green" id="freeBtn" onclick="claimFreeTrial()">立即开通</button>
</div>
<div class="plan">
<div class="plan-top"><h3>月付</h3><span class="badge gray">即将上线</span></div>
<div class="price"><span class="price-pending">价格开放前公布</span></div>
<p class="desc">适合短期体验和灵活使用。</p>
<div class="plan-spacer"></div>
<button class="btn-plan disabled">即将上线</button>
</div>
<div class="plan">
<div class="plan-top"><h3>季付</h3><span class="badge gray">即将上线</span></div>
<div class="price"><span class="price-pending">价格开放前公布</span></div>
<p class="desc">适合持续跟踪市场机会。</p>
<div class="plan-spacer"></div>
<button class="btn-plan disabled">即将上线</button>
</div>
<div class="plan">
<div class="plan-top"><h3>年付</h3><span class="badge gray">即将上线</span></div>
<div class="price"><span class="price-pending">价格开放前公布</span></div>
<p class="desc">适合长期使用和策略复盘。</p>
<div class="plan-spacer"></div>
<button class="btn-plan disabled">即将上线</button>
</div>
</div>
</div>
{% endblock %}
{% block extra_script %}
<script>
var $ = function(id){ return document.getElementById(id); };
// ====== USER ======
var currentUser = null;
function readablePlanName(code) {
var names = {
free_trial_1m: '免费体验',
monthly_usdt: '月付',
quarterly_usdt: '季付',
yearly_usdt: '年付'
};
return names[code] || code || '未开通';
}
async function loadUser() {
try {
var resp = await fetch('/api/auth/me');
if (!resp.ok) return;
var data = await resp.json();
currentUser = data.user;
var email = currentUser.email || '--';
$('userInitial').textContent = email.charAt(0).toUpperCase();
$('userEmailShort').textContent = email.length > 14 ? email.slice(0,12) + '…' : email;
$('ddEmail').textContent = email;
} catch(e) {}
}
function toggleUserMenu() {
$('userDropdown').classList.toggle('show');
}
document.addEventListener('click', function(e) {
if (!e.target.closest('.sidebar-user') && !e.target.closest('.user-dropdown')) $('userDropdown').classList.remove('show');
});
function showChangePwd() {
$('userDropdown').classList.remove('show');
$('pwdModal').classList.add('show');
$('oldPwd').value = ''; $('newPwd').value = ''; $('cfmPwd').value = ''; $('pwdMsg').textContent = '';
}
function closePwdModal() { $('pwdModal').classList.remove('show'); }
async function changePwd() {
var old = $('oldPwd').value, nw = $('newPwd').value, cf = $('cfmPwd').value;
if (!old || !nw) { $('pwdMsg').className = 'modal-msg err'; $('pwdMsg').textContent = '请填写所有字段'; return; }
if (nw.length < 8) { $('pwdMsg').className = 'modal-msg err'; $('pwdMsg').textContent = '新密码至少 8 位'; return; }
if (nw !== cf) { $('pwdMsg').className = 'modal-msg err'; $('pwdMsg').textContent = '两次密码不一致'; return; }
try {
var r = await fetch('/api/auth/change-password', {
method: 'POST', headers: {'Content-Type': 'application/json'},
body: JSON.stringify({old_password: old, new_password: nw})
});
var d = await r.json();
if (!r.ok) throw new Error(d.detail || '修改失败');
$('pwdMsg').className = 'modal-msg ok'; $('pwdMsg').textContent = d.message || '修改成功';
setTimeout(closePwdModal, 1200);
} catch(e) { $('pwdMsg').className = 'modal-msg err'; $('pwdMsg').textContent = e.message; }
}
async function doLogout() {
await fetch('/api/auth/logout', { method: 'POST' });
window.location.href = '/auth';
}
// ====== SUBSCRIPTION ======
async function post(url, body) {
var r = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body || {}) });
var data = await r.json().catch(function(){ return { detail: '请求失败' }; });
if (!r.ok) throw new Error(data.detail || '请求失败');
return data;
}
async function loadMe() {
try {
var r = await fetch('/api/auth/me');
if (!r.ok) { window.location.href = '/auth'; return; }
var data = await r.json();
var s = data.subscription;
var params = new URLSearchParams(location.search);
if (!s || params.get('welcome') === '1' || params.get('expired') === '1') {
document.getElementById('guideBox').classList.add('show');
if (params.get('expired') === '1') {
document.getElementById('guideTitle').textContent = '订阅已到期,请先续订';
document.getElementById('guideText').textContent = '当前账号没有有效订阅。续订或开通套餐后,才能继续访问看板、策略、迭代和舆情页面。';
} else if (!s) {
document.getElementById('guideTitle').textContent = '欢迎使用 AlphaX Agent Crypto请先开通套餐';
document.getElementById('guideText').textContent = '新用户可领取 30 天免费体验。开通后即可进入完整功能页面。';
}
}
document.getElementById('statusBar').style.display = 'flex';
document.getElementById('mePlan').textContent = s ? readablePlanName(s.plan_code) : '未开通';
document.getElementById('meEnd').textContent = s ? String(s.end_at).slice(0, 10) : '--';
if (s) {
document.getElementById('freeBtn').textContent = '已开通';
document.getElementById('freeBtn').className = 'btn-plan disabled';
document.getElementById('freeBtn').onclick = null;
}
} catch (e) {}
}
async function claimFreeTrial() {
try {
var data = await post('/api/subscriptions/free-trial');
var el = document.getElementById('trialMsg');
var sub = data.subscription;
el.className = 'msg ok'; el.textContent = '开通成功!到期: ' + String(sub.end_at).slice(0, 10);
document.getElementById('freeBtn').textContent = '已开通';
document.getElementById('freeBtn').className = 'btn-plan disabled';
document.getElementById('freeBtn').onclick = null;
document.getElementById('mePlan').textContent = readablePlanName(sub.plan_code);
document.getElementById('meEnd').textContent = String(sub.end_at).slice(0, 10);
setTimeout(function(){ window.location.href = '/app'; }, 900);
} catch (e) {
var el = document.getElementById('trialMsg');
el.className = 'msg err'; el.textContent = e.message;
}
}
loadUser();
loadMe();
</script>
{% endblock %}