/** * Stock Buddy 前端逻辑 */ const API_BASE = 'http://localhost:8000/api'; // 页面状态 let currentPage = 'dashboard'; let positions = []; // ═════════════════════════════════════════════════════════════════════ // 初始化 // ═════════════════════════════════════════════════════════════════════ document.addEventListener('DOMContentLoaded', () => { initNavigation(); initEventListeners(); loadDashboard(); }); function initNavigation() { document.querySelectorAll('.nav-item').forEach(item => { item.addEventListener('click', (e) => { e.preventDefault(); const page = item.dataset.page; switchPage(page); }); }); } function initEventListeners() { // 刷新按钮 document.getElementById('refresh-btn').addEventListener('click', () => { loadCurrentPage(); }); // 运行分析 document.getElementById('run-analysis-btn').addEventListener('click', async () => { await runDailyAnalysis(); }); // 添加持仓 document.getElementById('add-position-btn').addEventListener('click', () => { showModal('add-position-modal'); }); document.getElementById('cancel-add').addEventListener('click', () => { hideModal('add-position-modal'); }); document.querySelector('.modal-close').addEventListener('click', () => { hideModal('add-position-modal'); }); document.getElementById('add-position-form').addEventListener('submit', async (e) => { e.preventDefault(); await addPosition(); }); // 股票分析 document.getElementById('analyze-btn').addEventListener('click', async () => { const input = document.getElementById('stock-search').value.trim(); if (input) { await analyzeStock(input); } }); document.getElementById('stock-search').addEventListener('keypress', (e) => { if (e.key === 'Enter') { document.getElementById('analyze-btn').click(); } }); } // ═════════════════════════════════════════════════════════════════════ // 页面切换 // ═════════════════════════════════════════════════════════════════════ function switchPage(page) { currentPage = page; // 更新导航状态 document.querySelectorAll('.nav-item').forEach(item => { item.classList.remove('active'); if (item.dataset.page === page) { item.classList.add('active'); } }); // 切换页面内容 document.querySelectorAll('.page').forEach(p => p.classList.remove('active')); document.getElementById(`page-${page}`).classList.add('active'); // 更新标题 const titles = { dashboard: '总览', positions: '持仓管理', analysis: '股票分析', sentiment: '舆情监控', settings: '设置' }; document.getElementById('page-title').textContent = titles[page]; // 加载页面数据 loadCurrentPage(); } function loadCurrentPage() { switch (currentPage) { case 'dashboard': loadDashboard(); break; case 'positions': loadPositions(); break; case 'sentiment': loadSentiment(); break; } } // ═════════════════════════════════════════════════════════════════════ // 数据加载 // ═════════════════════════════════════════════════════════════════════ async function loadDashboard() { try { const response = await fetch(`${API_BASE}/positions`); positions = await response.json(); updateDashboardStats(); renderPositionsTable(); } catch (error) { console.error('加载失败:', error); showError('数据加载失败'); } } function updateDashboardStats() { const totalValue = positions.reduce((sum, p) => sum + (p.market_value || 0), 0); const totalCost = positions.reduce((sum, p) => sum + (p.shares * p.cost_price), 0); const totalPnl = totalValue - totalCost; const totalPnlPercent = totalCost > 0 ? (totalPnl / totalCost) * 100 : 0; document.getElementById('total-value').textContent = formatMoney(totalValue); document.getElementById('total-pnl').textContent = `${totalPnl >= 0 ? '+' : ''}${formatMoney(totalPnl)} (${totalPnlPercent.toFixed(2)}%)`; document.getElementById('total-pnl').className = `stat-change ${totalPnl >= 0 ? 'positive' : 'negative'}`; document.getElementById('position-count').textContent = positions.length; // 统计买入信号 const buySignals = positions.filter(p => p.pnl_percent < -5).length; // 简化逻辑 document.getElementById('buy-signals').textContent = buySignals; } function renderPositionsTable() { const tbody = document.getElementById('positions-tbody'); if (positions.length === 0) { tbody.innerHTML = '
请先添加持仓股票
'; return; } list.innerHTML = '舆情分析功能开发中...
'; } // ═════════════════════════════════════════════════════════════════════ // 持仓操作 // ═════════════════════════════════════════════════════════════════════ async function addPosition() { const data = { stock_name: document.getElementById('pos-name').value, ticker: document.getElementById('pos-ticker').value, shares: parseInt(document.getElementById('pos-shares').value), cost_price: parseFloat(document.getElementById('pos-cost').value), strategy: document.getElementById('pos-strategy').value, notes: document.getElementById('pos-notes').value }; try { const response = await fetch(`${API_BASE}/positions`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }); if (response.ok) { hideModal('add-position-modal'); document.getElementById('add-position-form').reset(); loadCurrentPage(); showSuccess('持仓添加成功'); } else { throw new Error('添加失败'); } } catch (error) { showError('添加失败: ' + error.message); } } async function deletePosition(id) { if (!confirm('确定删除此持仓吗?')) return; try { const response = await fetch(`${API_BASE}/positions/${id}`, { method: 'DELETE' }); if (response.ok) { loadPositions(); showSuccess('删除成功'); } } catch (error) { showError('删除失败'); } } // ═════════════════════════════════════════════════════════════════════ // 股票分析 // ═════════════════════════════════════════════════════════════════════ async function analyzeStock(input) { const resultDiv = document.getElementById('analysis-result'); resultDiv.classList.remove('hidden'); resultDiv.innerHTML = '分析失败: ${error.message}