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

220 lines
9.5 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" href="/subscription"><svg class="link-icon"><use href="#svg-subscribe"/></svg>订阅</a>
<a class="sidebar-link active" 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>
main { max-width: 680px; margin: 0 auto; width: 100%; padding: 32px 20px; display: flex; flex-direction: column; gap: 28px; }
.page-header { text-align: center; }
.page-header h1 { font-size: 28px; font-weight: 700; letter-spacing: -.5px; margin-bottom: 6px; }
.page-header .sub { font-size: 14px; color: var(--stone); }
/* Invite card */
.invite-card {
background: var(--canvas); border: 1px solid var(--hairline-soft);
border-radius: var(--radius-xl); padding: 28px 24px;
display: flex; flex-direction: column; gap: 16px;
}
.invite-label { font-size: 12px; color: var(--stone); font-weight: 600; text-transform: uppercase; letter-spacing: .5px; }
.invite-link-box {
display: flex; align-items: center; gap: 8px;
background: var(--surface); border: 1px solid var(--hairline);
border-radius: var(--radius-lg); padding: 10px 14px;
font-size: 14px; color: var(--ink); font-family: monospace;
word-break: break-all; user-select: all;
}
.invite-link-box .link-text { flex: 1; min-width: 0; }
.copy-btn {
flex-shrink: 0; padding: 7px 18px; border: 0; border-radius: var(--radius-full);
background: var(--primary); color: var(--on-primary);
font-size: 13px; font-weight: 600; cursor: pointer; transition: .15s;
}
.copy-btn:hover { opacity: .85; }
.copy-btn.copied { background: var(--green); }
.copy-msg { font-size: 12px; color: var(--green); min-height: 18px; text-align: center; }
/* Stats */
.stats-row { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.stat-box {
background: var(--canvas); border: 1px solid var(--hairline-soft);
border-radius: var(--radius-xl); padding: 20px;
text-align: center; display: flex; flex-direction: column; gap: 4px;
}
.stat-box .num { font-size: 36px; font-weight: 700; letter-spacing: -1px; line-height: 1; }
.stat-box .lbl { font-size: 12px; color: var(--stone); }
/* Referred list */
.ref-list { display: flex; flex-direction: column; gap: 8px; }
.ref-card {
background: var(--canvas); border: 1px solid var(--hairline-soft);
border-radius: var(--radius-lg); padding: 16px 18px;
display: flex; align-items: center; gap: 12px;
}
.ref-avatar {
width: 36px; height: 36px; border-radius: 50%;
background: var(--yellow); color: var(--primary);
display: grid; place-items: center; font-weight: 700; font-size: 14px; flex-shrink: 0;
}
.ref-info { flex: 1; min-width: 0; }
.ref-email { font-weight: 600; font-size: 14px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ref-date { font-size: 11px; color: var(--stone); margin-top: 2px; }
.ref-badge { flex-shrink: 0; font-size: 11px; font-weight: 600; padding: 3px 10px; border-radius: var(--radius-full); }
.ref-badge.verified { background: var(--green-light); color: var(--green); }
.ref-badge.pending { background: var(--yellow-light); color: var(--yellow-dark); }
.empty-state { text-align: center; padding: 48px 20px; color: var(--stone); }
.empty-state p { font-size: 14px; margin-bottom: 8px; }
.empty-state .empty-icon { font-size: 48px; margin-bottom: 16px; opacity: .3; }
/* Share section */
.share-section { text-align: center; }
.share-section h3 { font-size: 16px; font-weight: 600; margin-bottom: 4px; }
.share-section p { font-size: 13px; color: var(--stone); margin-bottom: 12px; }
.share-tips { display: flex; flex-direction: column; gap: 6px; text-align: left; font-size: 13px; color: var(--slate); line-height: 1.6; padding: 16px 20px; background: var(--surface); border-radius: var(--radius-lg); }
@media(max-width:480px){
main { padding: 20px 14px; gap: 20px; }
.invite-card { padding: 20px 16px; }
.invite-link-box { flex-direction: column; }
.copy-btn { width: 100%; }
.stats-row { grid-template-columns: 1fr; }
}
</style>
{% endblock %}
{% block content %}
<main>
<div class="page-header">
<h1>🎁 推荐好友</h1>
<p class="sub">邀请朋友加入 AlphaX Agent Crypto一起跟踪市场信号</p>
</div>
<div class="invite-card">
<div class="invite-label">你的专属邀请链接</div>
<div class="invite-link-box">
<span class="link-text" id="inviteLink">加载中...</span>
<button class="copy-btn" id="copyBtn" onclick="copyLink()">复制链接</button>
</div>
<div class="copy-msg" id="copyMsg"></div>
</div>
<div class="stats-row" id="statsRow">
<div class="stat-box"><div class="num">--</div><div class="lbl">已邀请</div></div>
<div class="stat-box"><div class="num">--</div><div class="lbl">已注册</div></div>
</div>
<div id="refSection">
<h3 style="font-size:16px;font-weight:600;margin-bottom:12px">已邀请好友</h3>
<div class="ref-list" id="refList">
<div class="empty-state"><p>加载中...</p></div>
</div>
</div>
<div class="share-section">
<h3>如何推荐</h3>
<p>复制邀请链接发送给朋友,他们注册时将自动绑定你的邀请码</p>
<div class="share-tips">
<div>📋 复制邀请链接,通过微信 / Telegram / 邮件分享</div>
<div>🔗 好友点击链接注册,系统自动记录邀请关系</div>
<div>📊 你可以在本页查看邀请进度</div>
</div>
</div>
</main>
{% endblock %}
{% block extra_script %}
<script>
var inviteCode = '';
var baseUrl = window.location.origin;
async function init() {
try {
var r = await fetch('/api/auth/me');
if (!r.ok) { window.location.href = '/auth'; return; }
var data = await r.json();
inviteCode = data.user.invite_code || '';
if (!inviteCode) {
document.getElementById('inviteLink').textContent = '暂无邀请码';
return;
}
var link = baseUrl + '/auth?invite=' + inviteCode;
document.getElementById('inviteLink').textContent = link;
loadReferralStats();
} catch(e) {
document.getElementById('inviteLink').textContent = '加载失败,请刷新重试';
}
}
async function loadReferralStats() {
try {
var r = await fetch('/api/referral/stats');
if (!r.ok) return;
var d = await r.json();
document.getElementById('statsRow').innerHTML =
'<div class="stat-box"><div class="num">' + (d.total_invited || 0) + '</div><div class="lbl">已邀请</div></div>' +
'<div class="stat-box"><div class="num">' + (d.total_registered || 0) + '</div><div class="lbl">已注册</div></div>';
renderRefList(d.invited_users || []);
} catch(e) {}
}
function renderRefList(users) {
var list = document.getElementById('refList');
if (!users.length) {
list.innerHTML = '<div class="empty-state"><div class="empty-icon">📭</div><p>还没有好友通过你的链接注册</p><p style="font-size:12px;color:var(--muted)">分享邀请链接,开始推荐吧</p></div>';
return;
}
list.innerHTML = users.map(function(u) {
var initial = (u.email || '?').charAt(0).toUpperCase();
var badge = u.email_verified
? '<span class="ref-badge verified">已验证</span>'
: '<span class="ref-badge pending">未验证</span>';
var date = u.created_at ? u.created_at.slice(0, 10) : '--';
return '<div class="ref-card">' +
'<div class="ref-avatar">' + initial + '</div>' +
'<div class="ref-info"><div class="ref-email">' + esc(u.email) + '</div><div class="ref-date">' + date + '</div></div>' +
badge +
'</div>';
}).join('');
}
function copyLink() {
var link = document.getElementById('inviteLink').textContent;
if (!link || link === '加载中...' || link === '暂无邀请码') return;
navigator.clipboard.writeText(link).then(function() {
var btn = document.getElementById('copyBtn');
var msg = document.getElementById('copyMsg');
btn.textContent = '已复制 ✓';
btn.classList.add('copied');
msg.textContent = '邀请链接已复制到剪贴板';
setTimeout(function() {
btn.textContent = '复制链接';
btn.classList.remove('copied');
msg.textContent = '';
}, 2000);
}).catch(function() {
var msg = document.getElementById('copyMsg');
msg.textContent = '复制失败,请手动选择链接复制';
msg.style.color = 'var(--red)';
});
}
function esc(s) { return String(s||'').replace(/[&<>"]/g, function(c){ return {'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;'}[c] }); }
init();
</script>
{% endblock %}