@@ -286,6 +332,7 @@ async function refreshAll() {
portfolioData = portfolio;
watchlistData = watchlist;
reportsData = Array.isArray(reports) ? reports : [];
+ renderAlerts(overview && overview.alerts ? overview.alerts : []);
// 右上角时间显示:来源时间说明
let timeParts = ['⏱页面 ' + new Date().toLocaleTimeString('zh-CN', {hour:'2-digit',minute:'2-digit'})];
if (watchData && watchData.data_time) {
@@ -2621,6 +2668,76 @@ function showStrategyResult(data) {
}
+
+// ── 浮动异常区逻辑(2026-08-12)──
+let lastAlertKeys = '';
+let alertAutoHideTimer = null;
+
+function toggleAlertFloat(ev) {
+ if (ev) ev.stopPropagation();
+ const el = document.getElementById('alertFloat');
+ const body = document.getElementById('alertBody');
+ const isCollapsed = el.classList.contains('collapsed');
+ el.classList.toggle('collapsed');
+ body.style.display = isCollapsed ? 'block' : 'none';
+ el.querySelector('.alert-toggle').textContent = isCollapsed ? '▴' : '▾';
+ if (alertAutoHideTimer) { clearTimeout(alertAutoHideTimer); alertAutoHideTimer = null; }
+}
+
+function renderAlerts(alerts) {
+ const el = document.getElementById('alertFloat');
+ const body = document.getElementById('alertBody');
+ const countEl = document.getElementById('alertCount');
+ const list = Array.isArray(alerts) ? alerts : [];
+ // 只显示 error/critical/warning(info 不打扰)
+ const shown = list.filter(a => a.level !== 'info');
+ const keys = shown.map(a => a.ts + '_' + a.source + '_' + a.code).join('|');
+
+ if (!shown.length) {
+ el.classList.add('collapsed');
+ body.style.display = 'none';
+ el.querySelector('.alert-toggle').textContent = '▾';
+ countEl.style.display = 'none';
+ countEl.textContent = '0';
+ lastAlertKeys = '';
+ return;
+ }
+
+ // 新异常 → 自动展开几秒后折叠
+ const isNew = keys !== lastAlertKeys;
+ lastAlertKeys = keys;
+
+ countEl.style.display = 'inline-block';
+ countEl.textContent = shown.length;
+
+ const levelIcon = { critical: '🔴', error: '🟠', warning: '🟡' };
+ body.innerHTML = shown.map(a =>
+ `
+
${levelIcon[a.level]||'🟠'} ${a.source||''}
+ ${a.code ? `${a.code}` : ''}
+
${a.title || ''}
+ ${a.detail ? `
${a.detail}
` : ''}
+
${a.ts_str || ''}
+
`
+ ).join('');
+
+ // 折叠态且新消息 → 展开 5 秒自动收起
+ if (isNew && el.classList.contains('collapsed')) {
+ el.classList.remove('collapsed');
+ body.style.display = 'block';
+ el.querySelector('.alert-toggle').textContent = '▴';
+ if (alertAutoHideTimer) clearTimeout(alertAutoHideTimer);
+ alertAutoHideTimer = setTimeout(() => {
+ el.classList.add('collapsed');
+ body.style.display = 'none';
+ el.querySelector('.alert-toggle').textContent = '▾';
+ alertAutoHideTimer = null;
+ }, 5000);
+ }
+}
+
+// refreshAll 内联渲染:把 alerts 交给 renderAlerts
+