From abef51b805adb4ea354d5d8fe56a7f52d638cebd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=85=AC=E6=98=8E?= <83812544+Ed1s0nZ@users.noreply.github.com> Date: Tue, 9 Jun 2026 18:05:29 +0800 Subject: [PATCH] Add files via upload --- web/static/css/style.css | 50 +++++++++++++++++++++++++++----------- web/static/js/dashboard.js | 42 +++++++++++++++++--------------- 2 files changed, 59 insertions(+), 33 deletions(-) diff --git a/web/static/css/style.css b/web/static/css/style.css index d59f0b5c..8c760600 100644 --- a/web/static/css/style.css +++ b/web/static/css/style.css @@ -16049,17 +16049,10 @@ tr.mcp-stats-tool-row[data-tool-name]:focus-visible { gap: 8px; } -.dashboard-recent-facts-meta { - font-size: 0.75rem; - color: var(--text-secondary, #6b7280); - padding: 2px 4px 8px; - font-variant-numeric: tabular-nums; -} - .dashboard-recent-fact-item { display: grid; - /* 置顶 / 分类 / 置信度 固定列宽,保证各行对齐 */ - grid-template-columns: 20px 64px 56px minmax(0, 1.4fr) minmax(0, 1fr) 9.5rem; + /* 置顶 / 分类 / 置信度 / 摘要 / fact_key / 项目 / 时间 */ + grid-template-columns: 20px 64px 56px minmax(0, 1.2fr) minmax(0, 1fr) 72px 9.5rem; align-items: center; column-gap: 10px; padding: 12px 10px; @@ -16097,6 +16090,35 @@ tr.mcp-stats-tool-row[data-tool-name]:focus-visible { justify-self: center; } +.dashboard-recent-fact-project { + display: inline-flex; + align-items: center; + justify-content: center; + width: 72px; + box-sizing: border-box; + padding: 3px 6px; + border-radius: 6px; + font-size: 0.6875rem; + font-weight: 600; + line-height: 1.2; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + justify-self: start; + background: #fff; + border: 1px dashed transparent; +} + +/* 项目:虚线描边 + 浅底,与置信度实心药丸区分 */ +.dashboard-recent-fact-project.proj-tone-0 { background: rgba(139, 92, 246, 0.05); color: #6d28d9; border-color: rgba(109, 40, 217, 0.45); } +.dashboard-recent-fact-project.proj-tone-1 { background: rgba(59, 130, 246, 0.05); color: #1d4ed8; border-color: rgba(29, 78, 216, 0.45); } +.dashboard-recent-fact-project.proj-tone-2 { background: rgba(20, 184, 166, 0.05); color: #0f766e; border-color: rgba(15, 118, 110, 0.45); } +.dashboard-recent-fact-project.proj-tone-3 { background: rgba(245, 158, 11, 0.06); color: #b45309; border-color: rgba(180, 83, 9, 0.45); } +.dashboard-recent-fact-project.proj-tone-4 { background: rgba(244, 63, 94, 0.05); color: #be123c; border-color: rgba(190, 18, 60, 0.45); } +.dashboard-recent-fact-project.proj-tone-5 { background: rgba(99, 102, 241, 0.05); color: #4338ca; border-color: rgba(67, 56, 202, 0.45); } +.dashboard-recent-fact-project.proj-tone-6 { background: rgba(16, 185, 129, 0.05); color: #047857; border-color: rgba(4, 120, 87, 0.45); } +.dashboard-recent-fact-project.proj-tone-7 { background: rgba(236, 72, 153, 0.05); color: #be185d; border-color: rgba(190, 24, 93, 0.45); } + .dashboard-recent-fact-cat, .dashboard-recent-fact-conf { display: inline-flex; @@ -16165,7 +16187,7 @@ tr.mcp-stats-tool-row[data-tool-name]:focus-visible { white-space: nowrap; } -.dashboard-recent-fact-meta { +.dashboard-recent-fact-key { color: var(--text-secondary); font-size: 0.8125rem; min-width: 0; @@ -16188,21 +16210,21 @@ tr.mcp-stats-tool-row[data-tool-name]:focus-visible { @media (max-width: 900px) { .dashboard-recent-fact-item { - grid-template-columns: 20px 64px minmax(0, 1fr) auto 8.25rem; + grid-template-columns: 20px 64px minmax(0, 1fr) minmax(0, 0.8fr) 72px 8.25rem; } .dashboard-recent-fact-conf { display: none; } } @media (max-width: 720px) { .dashboard-recent-fact-item { - grid-template-columns: 20px 64px minmax(0, 1fr) auto; + grid-template-columns: 20px 64px minmax(0, 1fr) 72px auto; } - .dashboard-recent-fact-meta { display: none; } + .dashboard-recent-fact-key { display: none; } } @media (max-width: 480px) { .dashboard-recent-fact-item { - grid-template-columns: 20px minmax(0, 1fr) auto; + grid-template-columns: 20px 72px minmax(0, 1fr) auto; } .dashboard-recent-fact-cat { display: none; } .dashboard-recent-fact-time { display: none; } diff --git a/web/static/js/dashboard.js b/web/static/js/dashboard.js index 457bc7a7..8eb3c32d 100644 --- a/web/static/js/dashboard.js +++ b/web/static/js/dashboard.js @@ -116,7 +116,7 @@ async function refreshDashboard() { fetchJson('/api/monitor/stats'), fetchJson('/api/knowledge/stats'), fetchJson('/api/skills/stats'), - fetchJson('/api/vulnerabilities?limit=5&page=1'), + fetchJson('/api/vulnerabilities?limit=10&page=1'), fetchJson('/api/roles'), fetchJson('/api/multi-agent/markdown-agents'), openVulnQuery('critical'), @@ -139,7 +139,7 @@ async function refreshDashboard() { fetchJson('/api/c2/listeners'), fetchJson('/api/c2/sessions?limit=500'), fetchJson('/api/c2/tasks?page=1&page_size=1'), - fetchJson('/api/projects/dashboard-summary?fact_limit=5') + fetchJson('/api/projects/dashboard-summary?fact_limit=10') ]); // 如果在 await 期间 controller 已被 abort,说明又有新刷新启动了,丢弃本次结果 @@ -1117,7 +1117,7 @@ function renderRecentVulns(res) { empty.classList.remove('is-rich'); } - list.slice(0, 5).forEach(function (v) { + list.slice(0, 10).forEach(function (v) { const sev = (v.severity || 'info').toLowerCase(); const status = (v.status || 'open').toLowerCase(); const item = document.createElement('a'); @@ -1211,6 +1211,18 @@ function factCategoryShortLabel(category) { return raw || 'note'; } +// 按 project_id(回退 project_name)稳定映射 8 种配色,同一项目跨刷新颜色一致 +function projectFactProjectTone(projectId, projectName) { + var key = String(projectId || projectName || '').trim(); + if (!key) return 0; + var hash = 0; + for (var i = 0; i < key.length; i++) { + hash = ((hash << 5) - hash) + key.charCodeAt(i); + hash |= 0; + } + return Math.abs(hash) % 8; +} + function openProjectFactFromDashboard(projectId, factKey) { if (!projectId) return; if (typeof switchPage === 'function') { @@ -1289,15 +1301,7 @@ function renderRecentFacts(res) { empty.classList.remove('is-rich'); } - if (activeProjects > 0 || totalFacts > 0) { - var meta = document.createElement('div'); - meta.className = 'dashboard-recent-facts-meta'; - meta.textContent = dt('dashboard.factsAcrossProjects', { count: activeProjects, facts: totalFacts }, - activeProjects + ' 个活跃项目 · ' + totalFacts + ' 条事实'); - wrap.appendChild(meta); - } - - list.slice(0, 5).forEach(function (f) { + list.slice(0, 10).forEach(function (f) { if (!f) return; var category = factCategoryShortLabel(f.category); var confidence = String(f.confidence || 'tentative').toLowerCase(); @@ -1319,17 +1323,17 @@ function renderRecentFacts(res) { var pinMark = ''; + var projectLabel = (f.project_name || '').trim() || dt('projects.defaultProjectName', null, '项目'); + var factKeyLabel = (f.fact_key || '').trim() || '—'; + var projectTone = projectFactProjectTone(pid, projectLabel); + var projectCol = '' + esc(projectLabel) + ''; var categoryBadge = '' + esc(category) + ''; var confBadge = '' + esc(factConfidenceShortLabel(confidence)) + ''; var summary = '' + esc(f.summary || dt('common.untitled', null, '无标题')) + ''; - // 勿用 i18n 插值拼接 fact_key:i18next 会把 / 转成 / 导致乱码 - var projectLabel = (f.project_name || '').trim() || dt('projects.defaultProjectName', null, '项目'); - var factKeyLabel = (f.fact_key || '').trim() || '—'; - var metaText = projectLabel + ' · ' + factKeyLabel; - var metaLine = '' + esc(metaText) + ''; + var factKeyCol = '' + esc(factKeyLabel) + ''; var time = '' + esc(timeAgoStr(f.updated_at)) + ''; - item.innerHTML = pinMark + categoryBadge + confBadge + summary + metaLine + time; + item.innerHTML = pinMark + categoryBadge + confBadge + summary + factKeyCol + projectCol + time; wrap.appendChild(item); }); } @@ -1428,7 +1432,7 @@ function renderVulnStatusPanel(byStatus, total) { // // bySeverityOpen: { critical, high, medium, low }(只统计 status=open 的漏洞;info 不计入) // totalOpen: 待处理漏洞总数(= critical + high + medium + low),仅用于"全无待处理 → safe"判断 -// recentVulnsRes: /api/vulnerabilities?limit=5 响应(用于"最近发现"时间,口径是全量,与处置状态无关) +// recentVulnsRes: /api/vulnerabilities?limit=10 响应(用于"最近发现"时间,口径是全量,与处置状态无关) function renderSeverityInsights(bySeverityOpen, totalOpen, recentVulnsRes) { var riskBox = document.querySelector('.dashboard-severity-insight-risk'); var levelEl = document.getElementById('dashboard-severity-risk-level');