前端浮动异常区: 右上角固定,新异常自动展开5秒折叠,只显示active异常,点击切换展开/折叠; title知微情报
This commit is contained in:
+118
-1
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>MoFin · 莫荷情报</title>
|
||||
<title>MoFin · 知微情报</title>
|
||||
<script src="/tailwind.js"></script>
|
||||
<script src="/chart.min.js"></script>
|
||||
<style>
|
||||
@@ -40,6 +40,43 @@ body { background: #0f0f13; color: #e2e8f0; }
|
||||
50% { opacity: 0.3; }
|
||||
}
|
||||
.pulse-warn { animation: pulse-warn 1.2s ease-in-out infinite; }
|
||||
|
||||
#alertFloat {
|
||||
position: fixed; top: 12px; right: 12px; z-index: 9999;
|
||||
max-width: 380px; min-width: 260px;
|
||||
background: rgba(26,26,36,0.95); border: 1px solid #7f1d1d;
|
||||
border-radius: 12px; box-shadow: 0 8px 30px rgba(0,0,0,0.6);
|
||||
font-size: 12px; color: #e2e8f0; overflow: hidden;
|
||||
transition: all 0.25s ease;
|
||||
}
|
||||
#alertFloat.collapsed { min-width: 0; background: transparent; border-color: transparent; box-shadow: none; }
|
||||
#alertFloat .alert-head {
|
||||
display: flex; align-items: center; justify-content: space-between;
|
||||
padding: 8px 12px; cursor: pointer; user-select: none;
|
||||
background: rgba(127,29,29,0.35); border-bottom: 1px solid rgba(127,29,29,0.4);
|
||||
}
|
||||
#alertFloat.collapsed .alert-head { background: #7f1d1d; border-bottom: none; border-radius: 12px; }
|
||||
#alertFloat .alert-title { font-weight: 600; font-size: 12px; display: flex; align-items: center; gap: 6px; }
|
||||
#alertFloat .alert-count {
|
||||
background: #ef4444; color: white; border-radius: 999px;
|
||||
padding: 1px 8px; font-size: 11px; font-weight: 700;
|
||||
}
|
||||
#alertFloat .alert-toggle { color: #fca5a5; font-size: 14px; }
|
||||
#alertFloat .alert-body { max-height: 320px; overflow-y: auto; padding: 6px 0; }
|
||||
#alertFloat .alert-item {
|
||||
padding: 6px 12px; border-bottom: 1px solid rgba(255,255,255,0.06);
|
||||
line-height: 1.5;
|
||||
}
|
||||
#alertFloat .alert-item:last-child { border-bottom: none; }
|
||||
#alertFloat .alert-item .a-src { color: #fbbf24; font-size: 11px; font-weight: 600; }
|
||||
#alertFloat .alert-item .a-title { color: #fecaca; font-weight: 600; }
|
||||
#alertFloat .alert-item .a-detail { color: #94a3b8; font-size: 11px; }
|
||||
#alertFloat .alert-item.critical { border-left: 3px solid #ef4444; }
|
||||
#alertFloat .alert-item.error { border-left: 3px solid #f97316; }
|
||||
#alertFloat .alert-item.warning { border-left: 3px solid #eab308; }
|
||||
@keyframes alert-pop { 0% { opacity: 0; transform: translateY(-8px); } 100% { opacity: 1; transform: translateY(0); } }
|
||||
#alertFloat.new-alert { animation: alert-pop 0.3s ease; }
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body class="min-h-screen">
|
||||
@@ -129,6 +166,15 @@ body { background: #0f0f13; color: #e2e8f0; }
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 浮动异常区(右上角,任何板块可见,2026-08-12 老莫指示) -->
|
||||
<div id="alertFloat" class="collapsed" onclick="toggleAlertFloat(event)">
|
||||
<div class="alert-head">
|
||||
<span class="alert-title">⚠️ 异常 <span id="alertCount" class="alert-count" style="display:none">0</span></span>
|
||||
<span class="alert-toggle">▾</span>
|
||||
</div>
|
||||
<div id="alertBody" class="alert-body" style="display:none"></div>
|
||||
</div>
|
||||
|
||||
<!-- Strategy History Modal -->
|
||||
<div id="strategyModal" class="fixed inset-0 modal-overlay hidden items-center justify-center z-50" onclick="if(event.target===this)closeStrategyModal()">
|
||||
<div class="bg-[#1a1a24] border border-slate-700 rounded-2xl w-full max-w-2xl max-h-[85vh] overflow-y-auto mx-4 p-6" onclick="event.stopPropagation()">
|
||||
@@ -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 =>
|
||||
`<div class="alert-item ${a.level || 'error'}">
|
||||
<div><span class="a-src">${levelIcon[a.level]||'🟠'} ${a.source||''}</span>
|
||||
${a.code ? `<span class="font-mono text-slate-500">${a.code}</span>` : ''}</div>
|
||||
<div class="a-title">${a.title || ''}</div>
|
||||
${a.detail ? `<div class="a-detail">${a.detail}</div>` : ''}
|
||||
<div class="text-slate-600 text-[10px]">${a.ts_str || ''}</div>
|
||||
</div>`
|
||||
).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
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user