/* 自动化监控页:舆情驱动 + 持仓跟踪 双选项卡 */ let autoSettings = null; /* ===================== 选项卡 ===================== */ $$('.tab-item').forEach(t => t.onclick = () => { $$('.tab-item').forEach(x => x.classList.toggle('active', x === t)); $$('.tab-panel').forEach(p => p.style.display = (p.id === 'panel-' + t.dataset.tab) ? '' : 'none'); }); /* ===================== 加载 ===================== */ async function loadAuto() { try { autoSettings = await api('/api/settings'); const mono = autoSettings.monitor, tr = autoSettings.tracking; // 舆情 $('#monitorEnabled').checked = !!mono.enabled; $('#monitorInterval').value = mono.interval_min; $('#monitorImportance').value = mono.importance_threshold; $('#monitorSentiment').value = mono.sentiment_weight; $('#monitorKeywords').value = (mono.keywords || []).join(','); const cats = mono.categories || []; $$('#monitorCats input').forEach(c => c.checked = cats.includes(c.value)); renderMonitorState(autoSettings.state); // 静默期(舆情) const mq = (autoSettings.quiet && autoSettings.quiet.monitor) || {}; $('#monitorQuietEnabled').checked = !!mq.enabled; $('#monitorQuietRanges').value = mq.ranges || ''; // 持仓跟踪 $('#trackingEnabled').checked = !!tr.enabled; $('#trackingInterval').value = tr.interval_min; $('#trackingImpact').value = tr.impact_threshold; $('#trackingNotify').checked = !!tr.notify; renderTrackingState(tr); // 静默期(跟踪) const tq = (autoSettings.quiet && autoSettings.quiet.tracking) || {}; $('#trackingQuietEnabled').checked = !!tq.enabled; $('#trackingQuietRanges').value = tq.ranges || ''; loadLog(); loadTrackReports(); loadPositions(); loadTargets(); } catch (e) { toast('加载失败'); } } function renderMonitorState(state) { $('#monitorState').innerHTML = `
已处理新闻水位
#${state.last_news_id || 0}
上次扫描
${state.last_scan || '—'}
上次通知
${state.last_sent_count || 0} 条
`; } function renderTrackingState(tr) { $('#trackingState').innerHTML = `
自动跟踪
${tr.enabled ? '✅ 开启' : '⏸ 关闭'}(每 ${tr.interval_min} 分钟)
上次运行
${tr.last_run || '—'}
最近跟踪
${tr.last_stock || '—'}
最近告警影响度
${tr.last_alert ? tr.last_alert + '/100' : '—'}
`; } function collectMonitor() { const cats = $$('#monitorCats input:checked').map(c => c.value); return { monitor_enabled: $('#monitorEnabled').checked, monitor_interval: $('#monitorInterval').value, monitor_importance: $('#monitorImportance').value, monitor_sentiment: $('#monitorSentiment').value, monitor_categories: cats.join(','), monitor_keywords: $('#monitorKeywords').value }; } function collectMonitorQuiet() { return { enabled: $('#monitorQuietEnabled').checked, ranges: $('#monitorQuietRanges').value.trim() }; } function collectTrackingQuiet() { return { enabled: $('#trackingQuietEnabled').checked, ranges: $('#trackingQuietRanges').value.trim() }; } /* ===================== 保存 ===================== */ async function saveMonitor() { try { const r = await api('/api/settings', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ monitor: collectMonitor(), quiet: { monitor: collectMonitorQuiet() } }) }); toast(r.msg || '已保存'); } catch (e) { toast('保存失败:' + e.message); } } async function saveTracking() { try { const r = await api('/api/settings', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ tracking: { tracking_enabled: $('#trackingEnabled').checked, tracking_interval: $('#trackingInterval').value, tracking_impact_threshold: $('#trackingImpact').value, tracking_notify: $('#trackingNotify').checked }, quiet: { tracking: collectTrackingQuiet() } }) }); toast(r.msg || '已保存'); } catch (e) { toast('保存失败:' + e.message); } } /* ===================== 动作 ===================== */ async function scanNow() { const btn = event.target; btn.disabled = true; try { const r = await api('/api/monitor/scan', { method: 'POST' }); toast(`扫描完成:检查 ${r.checked} 条,重要 ${r.important} 条,发送 ${r.sent} 条`); loadAuto(); } catch (e) { toast('扫描失败:' + e.message); } btn.disabled = false; } async function runTrackAll() { const btn = event.target; btn.disabled = true; try { const r = await api('/api/tracking/run', { method: 'POST' }); toast(r.msg || '已启动'); setTimeout(loadTrackReports, 5000); setTimeout(loadTrackReports, 30000); setTimeout(loadTrackReports, 70000); } catch (e) { toast('启动失败:' + e.message); } btn.disabled = false; } /* ===================== 持仓(自选股)管理 ===================== */ async function loadPositions() { try { const d = await api('/api/watchlist'); const items = d.items || []; if (!items.length) { $('#posManage').innerHTML = '
暂无持仓。在下方搜索添加,或到 股票池 / 个股详情页 ⭐ 添加
'; return; } $('#posManage').innerHTML = ` ${items.map(p => ``).join('')}
股票现价今日评分操作
${p.name}
${p.code} · ${p.industry}
${p.close} ${fmtPct(p.change_pct)} ${p.score}
`; } catch (e) { $('#posManage').innerHTML = '
加载失败
'; } } async function addPos() { const kw = $('#posSearch').value.trim(); if (!kw) return; try { const d = await api('/api/stocks?keyword=' + encodeURIComponent(kw) + '&per=5'); const items = d.items || []; if (!items.length) { toast('未找到该股票'); return; } // 精确匹配或取第一个 const hit = items.find(x => x.code === kw || x.name === kw) || items[0]; await api('/api/watchlist/' + hit.code, { method: 'POST' }); toast(`已添加 ${hit.name} 为持仓`); $('#posSearch').value = ''; loadPositions(); } catch (e) { toast('添加失败'); } } async function removePos(code) { try { await api('/api/watchlist/' + code, { method: 'DELETE' }); toast('已移除持仓'); loadPositions(); } catch (e) { toast('操作失败'); } } /* ===================== 概念/主题/股票目标管理 ===================== */ async function loadTargets() { try { const d = await api('/api/targets'); const items = d.items || []; if (!items.length) { $('#tgtManage').innerHTML = '
暂无跟踪目标,添加概念/主题(如 AI算力、低空经济)或个股目标
'; return; } $('#tgtManage').innerHTML = ` ${items.map(t => ``).join('')}
类型目标检索关键词状态操作
${t.type === 'concept' ? '概念/主题' : '股票'} ${escapeHtml(t.name)}${t.code ? `
${t.code}
` : ''}
${escapeHtml(t.keywords || '—')} ${t.enabled ? '跟踪中' : '停用'}
`; } catch (e) { $('#tgtManage').innerHTML = '
加载失败
'; } } async function addTarget() { const type = $('#tgtType').value; const name = $('#tgtName').value.trim(); const keywords = $('#tgtKeywords').value.trim(); if (!name) { toast('请输入目标名称'); return; } try { await api('/api/targets', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ type, name, keywords }) }); toast('已添加跟踪目标'); $('#tgtName').value = ''; $('#tgtKeywords').value = ''; loadTargets(); } catch (e) { toast('添加失败:' + e.message); } } async function removeTarget(id) { try { await api('/api/targets/' + id, { method: 'DELETE' }); toast('已移除目标'); loadTargets(); } catch (e) { toast('操作失败'); } } /* ===================== 日志与报告 ===================== */ async function loadLog() { try { const d = await api('/api/monitor/log'); const items = d.items || []; if (!items.length) return; $('#logTb').innerHTML = items.map(n => ` ${n.sent_at} ${escapeHtml(n.title)} ${n.category} ${n.sentiment > 0 ? '利好' : n.sentiment < 0 ? '利空' : '中性'} (${Number(n.sentiment).toFixed(2)}) ${n.importance} ${n.status === 'sent' ? '已发送' : n.status === 'skipped' ? '已跳过' : n.status === 'quiet' ? '🌙 静默' : '失败'} `).join(''); } catch (e) {} } async function loadTrackReports() { try { const d = await api('/api/tracking'); const items = d.items || []; if (!items.length) return; $('#trackTb').innerHTML = items.slice(0, 10).map(r => { let meta = {}; try { meta = JSON.parse(r.meta || '{}'); } catch (e) {} const sigCls = meta.impact_score >= 65 ? 'tag-强烈推荐' : meta.impact_score >= 45 ? 'tag-推荐' : 'tag-关注'; const isConcept = r.target_type === 'concept'; return ` ${r.created_at} ${isConcept ? '概念' : '股票'} ${r.stock_name}${isConcept ? '' : `
${r.code}
`} ${meta.impact_score || '—'} ${meta.change_kind || '—'} ${escapeHtml(meta.summary || '')} `; }).join(''); } catch (e) {} } async function showTrack(id) { try { const d = await api('/api/tracking/' + id); const meta = d.meta || {}, src = d.sources || {}; const sigCls = meta.impact_score >= 65 ? 'tag-强烈推荐' : meta.impact_score >= 45 ? 'tag-推荐' : 'tag-关注'; const isConcept = d.target_type === 'concept'; // 概念报告:展示关键词/资讯/受益个股;股票报告:四环节数据源 let sourceHtml = ''; if (isConcept) { const dc = (meta.news_counts || {}).direct || 0, rc = (meta.news_counts || {}).rag || 0, nc = (meta.news_counts || {}).related || 0; sourceHtml = `
🔎 相关资讯(${dc} 条)
${escapeHtml(src.db_news || '')}
🧠 语义检索命中(${rc} 条)
${escapeHtml(src.rag_news || '')}
🏢 受益个股(${nc} 只)
${escapeHtml(src.related || '')}
`; } else { const newsBlock = (label, icon, seg) => { if (!seg) return ''; const txt = ((seg.db || '') + (seg.rag || '')).trim(); return `
${icon} ${label} ${txt.includes('[') ? txt.split('[').length - 1 : 0}
${escapeHtml(txt || '暂无')}
`; }; sourceHtml = newsBlock('个股直接动态', '📄', src.direct) + newsBlock('上游产业链(供给/成本)', '⬆️', src.upstream) + newsBlock('下游产业链(需求/景气)', '⬇️', src.downstream) + newsBlock('同业竞争', '🏢', src.peers); } openModal(`

${isConcept ? '🎯 概念主题跟踪' : '🧭 产业链跟踪'}:${d.stock_name}${d.code && !isConcept ? '(' + d.code + ')' : ''}

${isConcept ? '概念/主题' : d.industry} 影响度 ${meta.impact_score || '—'}/100 ${meta.change_kind || '—'} ${d.created_at}
${mdRender(d.report)}
数据源(智能体参考内容) ${sourceHtml}
${isConcept ? '' : `查看个股 →`}
`); } catch (e) { toast('加载失败'); } } loadAuto();