FA Research

FA 研究院

由本所交易引擎实时驱动 — 自动生成的市场快报与巨鲸活动报告,加上社区分析师公开发布的研究内容,全部数据可溯源。

2每日自动报告
0社区已发布
0累计阅读
0累计引用
查看链上数据看板

自动报告 · 由实时数据生成

每次访问根据 FA 行情与本机成交数据重新计算
加载中

订阅深度研报

每月一份 PDF 深度报告 · 涵盖宏观、链上、协议机制与机构资金流向 · 由 FA Research 分析师团队撰写

发布研究报告
0 / 300
报告详情
'; const w = window.open('', '_blank'); if (!w) { alert('请允许弹窗以导出 PDF'); return; } w.document.open(); w.document.write(html); w.document.close(); } // ====== Tab state ====== let currentTab = 'auto'; function renderList() { const host = document.getElementById('rs-list'); const title = document.getElementById('section-title'); const note = document.getElementById('section-note'); const reports = getReports(); let items = []; let mine = false; if (currentTab === 'auto') { title.textContent = '自动报告 · 由实时数据生成'; note.textContent = '每次访问根据 FA 行情与本机成交数据重新计算'; items = buildAutoReports(); } else if (currentTab === 'community') { title.textContent = '社区报告 · 公开发布'; note.textContent = '所有研究均存储于本机 localStorage · 由分析师署名负责'; items = [...reports].sort((a,b) => b.ts - a.ts); } else if (currentTab === 'mine') { title.textContent = '我的报告'; mine = true; const u = window.FAAccount && window.FAAccount.getCurrentUser ? window.FAAccount.getCurrentUser() : null; if (!u || !u.email) { note.textContent = '需要登录'; host.innerHTML = '
请先 登录 后查看你发布的研究报告。
'; return; } note.textContent = '已登录 ' + maskEmail(u.email); const myEmail = u.email.toLowerCase(); items = reports.filter(r => (r.authorEmail || '').toLowerCase() === myEmail) .sort((a,b) => b.ts - a.ts); } if (!items.length) { let msg = ''; if (currentTab === 'community') msg = '暂无社区报告 — 点击页面右上「发布研究报告」成为第一位贡献者。'; else if (currentTab === 'mine') msg = '你还没有发布过报告 — 点击「发布研究报告」开始。'; else msg = '数据准备中,请稍后'; host.innerHTML = '
' + msg + '
'; return; } host.innerHTML = items.map(r => cardHTML(r, { mine: mine })).join(''); // Bind clicks host.querySelectorAll('[data-open]').forEach(el => { el.addEventListener('click', () => openView(el.dataset.open)); }); host.querySelectorAll('[data-pdf]').forEach(b => { b.addEventListener('click', e => { e.stopPropagation(); const id = b.dataset.pdf; let rec; if (id.indexOf('auto:') === 0) { rec = buildAutoReports().find(r => r.id === id); } else { rec = getReports().find(r => r.id === id); } downloadAsPDF(rec); }); }); if (mine) { host.querySelectorAll('[data-edit]').forEach(b => { b.addEventListener('click', e => { e.stopPropagation(); openPublish(b.dataset.edit); }); }); host.querySelectorAll('[data-del]').forEach(b => { b.addEventListener('click', e => { e.stopPropagation(); if (!confirm('确认删除这份报告?该操作不可恢复。')) return; const all = getReports().filter(r => r.id !== b.dataset.del); setReports(all); try { localStorage.removeItem(KEY_CITES + b.dataset.del); } catch (e) {} renderList(); renderHeroStats(); }); }); } } // ====== View modal ====== function openView(id) { let rec; if (id.indexOf('auto:') === 0) { rec = buildAutoReports().find(r => r.id === id); } else { rec = getReports().find(r => r.id === id); if (rec) { // increment view rec.views = (Number(rec.views) || 0) + 1; const all = getReports().map(r => r.id === id ? rec : r); setReports(all); renderHeroStats(); // If we're on community/mine, update card view count silently if (currentTab !== 'auto') renderList(); } } if (!rec) return; const bg = document.getElementById('view-bg'); document.getElementById('vf-title').textContent = rec.title; const content = document.getElementById('vf-content'); const tag = rec.isAuto ? autoTag() : kindTag(rec.kind); const author = rec.isAuto ? 'FA Research Engine' : maskEmail(rec.authorEmail); const views = Number(rec.views) || 0; const cites = getCites(rec.id).length; // Auto reports: pre-built HTML body. // Community reports: Markdown source → marked.parse() (real renderer, falls back // to escaped plain text if FAMd not yet loaded from CDN). const renderedBody = (window.FAMd && window.FAMd.parse) ? window.FAMd.parse(rec.body || '') : escapeHTML(rec.body || ''); const bodyHTML = rec.isAuto ? rec.body : ('
' + renderedBody + '
'); const u = window.FAAccount && window.FAAccount.getCurrentUser ? window.FAAccount.getCurrentUser() : null; const myEmail = u && u.email ? u.email.toLowerCase() : null; const myCites = getCites(rec.id); const alreadyCited = myEmail && myCites.includes(myEmail); let footHTML = '
'; footHTML += ''; if (!rec.isAuto) { if (myEmail) { if (alreadyCited) { footHTML += '已引用'; } else { footHTML += ''; } footHTML += '' + cites + ' 人已引用'; } else { footHTML += '登录后可引用'; footHTML += '' + cites + ' 人已引用'; } } footHTML += '
'; content.innerHTML = '
' + tag + '' + escapeHTML(author) + ' · ' + fmtDateTime(rec.ts) + '' + '
' + '
' + '' + views.toLocaleString('en-US') + ' 阅读' + '·' + '' + cites + ' 引用' + '
' + '
' + escapeHTML(rec.summary || '') + '
' + bodyHTML + footHTML; const citeBtn = document.getElementById('vf-cite'); if (citeBtn) { citeBtn.addEventListener('click', () => { if (!myEmail) return; addCite(rec.id, myEmail); renderHeroStats(); openView(id); // re-render footer }); } const pdfBtn = document.getElementById('vf-pdf'); if (pdfBtn) { pdfBtn.addEventListener('click', () => downloadAsPDF(rec)); } bg.classList.add('open'); } // ====== Publish modal ====== let editingId = null; let mdEditor = null; function ensureMdEditor(initial) { const host = document.getElementById('pf-body-editor'); if (window.FAMd && typeof window.FAMd.mount === 'function') { if (mdEditor && mdEditor.element && mdEditor.element.isConnected) { mdEditor.setValue(initial || ''); } else { mdEditor = window.FAMd.mount(host, { value: initial || '', placeholder: '完整研究内容,支持 Markdown · 工具栏可插入图片(真实上传到 IPFS)', }); } } else { host.innerHTML = ''; document.getElementById('pf-body-fallback').value = initial || ''; mdEditor = { getValue: () => document.getElementById('pf-body-fallback').value, setValue: (s) => { document.getElementById('pf-body-fallback').value = s || ''; }, element: host, }; document.addEventListener('fa-md-ready', () => { const cur = mdEditor && mdEditor.getValue ? mdEditor.getValue() : ''; mdEditor = window.FAMd.mount(host, { value: cur, placeholder: '完整研究内容,支持 Markdown · 工具栏可插入图片(真实上传到 IPFS)', }); }, { once: true }); } } function openPublish(id) { const u = window.FAAccount && window.FAAccount.getCurrentUser ? window.FAAccount.getCurrentUser() : null; if (!u || !u.email) { alert('发布报告需要先登录'); if (window.FA?.gotoLogin) FA.gotoLogin(); else location.href = 'login.html'; return; } const bg = document.getElementById('publish-bg'); const titleEl = document.getElementById('pf-titleInput'); const kindEl = document.getElementById('pf-kind'); const sumEl = document.getElementById('pf-summary'); const errEl = document.getElementById('pf-err'); errEl.style.display = 'none'; if (id) { const rec = getReports().find(r => r.id === id); if (!rec) return; editingId = id; document.getElementById('pf-title').textContent = '编辑报告'; titleEl.value = rec.title || ''; kindEl.value = rec.kind || 'macro'; sumEl.value = rec.summary || ''; ensureMdEditor(rec.body || ''); } else { editingId = null; document.getElementById('pf-title').textContent = '发布研究报告'; titleEl.value = ''; kindEl.value = 'macro'; sumEl.value = ''; ensureMdEditor(''); } document.getElementById('pf-summary-count').textContent = sumEl.value.length; bg.classList.add('open'); } function submitPublish() { const u = window.FAAccount && window.FAAccount.getCurrentUser ? window.FAAccount.getCurrentUser() : null; if (!u || !u.email) return; const title = document.getElementById('pf-titleInput').value.trim(); const kind = document.getElementById('pf-kind').value; const summary = document.getElementById('pf-summary').value.trim(); const body = (mdEditor ? mdEditor.getValue() : '').trim(); const errEl = document.getElementById('pf-err'); if (!title) { errEl.textContent = '请填写标题'; errEl.style.display = 'block'; return; } if (title.length > 100) { errEl.textContent = '标题最多 100 字'; errEl.style.display = 'block'; return; } if (!summary) { errEl.textContent = '请填写摘要'; errEl.style.display = 'block'; return; } if (summary.length > 300) { errEl.textContent = '摘要最多 300 字'; errEl.style.display = 'block'; return; } if (!body || body.length < 20) { errEl.textContent = '正文至少 20 字'; errEl.style.display = 'block'; return; } if (!KIND_LABEL[kind]) { errEl.textContent = '请选择分类'; errEl.style.display = 'block'; return; } const all = getReports(); if (editingId) { const idx = all.findIndex(r => r.id === editingId); if (idx < 0) { errEl.textContent = '报告已被删除'; errEl.style.display = 'block'; return; } if ((all[idx].authorEmail || '').toLowerCase() !== u.email.toLowerCase()) { errEl.textContent = '只能编辑自己发布的报告'; errEl.style.display = 'block'; return; } all[idx] = Object.assign({}, all[idx], { title: title, kind: kind, summary: summary, body: body }); } else { const rec = { id: 'rs_' + Date.now().toString(36) + '_' + Math.random().toString(36).slice(2, 8), authorEmail: u.email, title: title, kind: kind, summary: summary, body: body, ts: Date.now(), views: 0, }; all.unshift(rec); } setReports(all); editingId = null; document.getElementById('publish-bg').classList.remove('open'); currentTab = 'mine'; document.querySelectorAll('.rs-tab').forEach(t => t.classList.toggle('active', t.dataset.tab === 'mine')); renderList(); renderHeroStats(); } // ====== Header chip ====== function renderHeaderChip() { const actions = document.getElementById('header-actions'); if (!actions) return; if (!(window.FAAccount && window.FAAccount.isLoggedIn && window.FAAccount.isLoggedIn())) return; const u = window.FAAccount.getCurrentUser(); if (!u) return; actions.innerHTML = '' + escapeHTML(u.email || '已登录') + '' + '账户' + ''; if (window.FA && window.FA.bindThemeToggles) window.FA.bindThemeToggles(); } // ====== Modal close binding ====== function bindModalClose() { document.querySelectorAll('[data-rs-close]').forEach(b => { b.addEventListener('click', () => { document.querySelectorAll('.rs-modal-bg').forEach(bg => bg.classList.remove('open')); }); }); document.querySelectorAll('.rs-modal-bg').forEach(bg => { bg.addEventListener('click', e => { if (e.target === bg) bg.classList.remove('open'); }); }); document.addEventListener('keydown', e => { if (e.key === 'Escape') { document.querySelectorAll('.rs-modal-bg').forEach(bg => bg.classList.remove('open')); } }); } // ====== Tab binding ====== function bindTabs() { document.querySelectorAll('.rs-tab').forEach(b => { b.addEventListener('click', () => { document.querySelectorAll('.rs-tab').forEach(x => x.classList.remove('active')); b.classList.add('active'); currentTab = b.dataset.tab; renderList(); }); }); } // ====== Boot ====== document.addEventListener('DOMContentLoaded', () => { if (window.FA && window.FA.bindThemeToggles) FA.bindThemeToggles(); bindTabs(); bindModalClose(); document.getElementById('open-publish').addEventListener('click', () => openPublish(null)); document.getElementById('pf-submit').addEventListener('click', submitPublish); document.getElementById('pf-summary').addEventListener('input', e => { document.getElementById('pf-summary-count').textContent = e.target.value.length; }); // Subscribe form — persist real email list for the marketing team. const subForm = document.getElementById('rs-sub-form'); if (subForm) { subForm.addEventListener('submit', e => { e.preventDefault(); const inp = document.getElementById('rs-sub-email'); const email = (inp.value || '').trim(); if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) { try { (window.FA && FA.toast) ? FA.toast('邮箱格式无效', false) : alert('邮箱格式无效'); } catch (_) { alert('邮箱格式无效'); } return; } try { const KEY = 'fa-waitlist:research-monthly'; const list = JSON.parse(localStorage.getItem(KEY) || '[]'); if (!list.find(x => x.email === email)) { list.unshift({ email, time: Date.now(), feature: 'research-monthly', label: 'FA Research 月度研报' }); localStorage.setItem(KEY, JSON.stringify(list.slice(0, 2000))); } } catch (_) {} inp.value = ''; try { (window.FA && FA.toast) ? FA.toast('订阅成功 — 下一份研报将通过邮件送达') : alert('订阅成功'); } catch (_) { alert('订阅成功'); } }); } renderHeroStats(); renderList(); // Refresh auto reports when market state updates if (window.FA && window.FA.subscribe) { let last = 0; window.FA.subscribe(() => { const now = Date.now(); if (now - last < 8000) return; last = now; if (currentTab === 'auto') renderList(); }); } // Wait for account engine then refresh header + my-tab view awaitAccount(() => { renderHeaderChip(); document.addEventListener('fa-account-login', () => { renderHeaderChip(); renderList(); }); document.addEventListener('fa-account-logout', () => { location.reload(); }); }); }); })();