mirror of
https://github.com/Ed1s0nZ/CyberStrikeAI.git
synced 2026-05-01 23:35:18 +02:00
Add files via upload
This commit is contained in:
+220
-3
@@ -14180,19 +14180,236 @@ header {
|
||||
}
|
||||
}
|
||||
|
||||
/* 漏洞严重程度分布:半环形图(浅色风格) */
|
||||
/* 漏洞严重程度分布:半环形图(浅色风格)
|
||||
三列布局:[风险概览卡(结论)] [donut(分布图)] [legend(明细)]
|
||||
—— 左列的「风险概览」填补原来 donut 左侧的留白,把"多危险 / 还有几个紧急项 / 多久前更新"这类
|
||||
结论性信息前置,与右列的分类明细互补。 */
|
||||
.dashboard-severity-wrap {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) minmax(200px, 260px);
|
||||
gap: 32px;
|
||||
grid-template-columns: minmax(160px, 180px) minmax(0, 1fr) minmax(200px, 260px);
|
||||
gap: 24px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
@media (max-width: 1100px) {
|
||||
.dashboard-severity-wrap {
|
||||
grid-template-columns: minmax(0, 1fr) minmax(200px, 260px);
|
||||
gap: 24px;
|
||||
}
|
||||
.dashboard-severity-insights {
|
||||
grid-column: 1 / -1;
|
||||
flex-direction: row;
|
||||
gap: 16px;
|
||||
}
|
||||
.dashboard-severity-insights > * {
|
||||
flex: 1 1 0;
|
||||
min-width: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 820px) {
|
||||
.dashboard-severity-wrap {
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
gap: 20px;
|
||||
}
|
||||
.dashboard-severity-insights {
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 风险概览卡:竖向堆叠三块小模块(风险等级/待处理/最新时间) */
|
||||
.dashboard-severity-insights {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 14px;
|
||||
align-self: stretch;
|
||||
justify-content: center;
|
||||
padding: 4px 0;
|
||||
}
|
||||
|
||||
/* —— 风险等级模块 —— */
|
||||
.dashboard-severity-insight-risk {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
padding: 12px 14px;
|
||||
border-radius: 12px;
|
||||
background: #fafbfc;
|
||||
border: 1px solid rgba(0, 0, 0, 0.05);
|
||||
transition: background 0.2s, border-color 0.2s;
|
||||
}
|
||||
|
||||
.dashboard-severity-insight-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.dashboard-severity-insight-label {
|
||||
font-size: 0.75rem;
|
||||
color: var(--text-secondary);
|
||||
font-weight: 500;
|
||||
letter-spacing: 0.02em;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.dashboard-severity-insight-risk-badge {
|
||||
font-size: 0.8125rem;
|
||||
font-weight: 700;
|
||||
padding: 2px 8px;
|
||||
border-radius: 999px;
|
||||
letter-spacing: 0.02em;
|
||||
line-height: 1.4;
|
||||
white-space: nowrap;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* 风险等级着色:安全/低/中/高/极高 */
|
||||
.dashboard-severity-insight-risk[data-level="safe"] { border-color: rgba(34, 197, 94, 0.20); background: rgba(34, 197, 94, 0.04); }
|
||||
.dashboard-severity-insight-risk[data-level="safe"] .dashboard-severity-insight-risk-badge { background: rgba(34, 197, 94, 0.12); color: #16a34a; }
|
||||
.dashboard-severity-insight-risk[data-level="low"] { border-color: rgba(59, 130, 246, 0.22); background: rgba(59, 130, 246, 0.04); }
|
||||
.dashboard-severity-insight-risk[data-level="low"] .dashboard-severity-insight-risk-badge { background: rgba(59, 130, 246, 0.12); color: #2563eb; }
|
||||
.dashboard-severity-insight-risk[data-level="medium"] { border-color: rgba(234, 179, 8, 0.25); background: rgba(234, 179, 8, 0.05); }
|
||||
.dashboard-severity-insight-risk[data-level="medium"] .dashboard-severity-insight-risk-badge { background: rgba(234, 179, 8, 0.15); color: #b45309; }
|
||||
.dashboard-severity-insight-risk[data-level="high"] { border-color: rgba(249, 115, 22, 0.28); background: rgba(249, 115, 22, 0.05); }
|
||||
.dashboard-severity-insight-risk[data-level="high"] .dashboard-severity-insight-risk-badge { background: rgba(249, 115, 22, 0.15); color: #c2410c; }
|
||||
.dashboard-severity-insight-risk[data-level="severe"] { border-color: rgba(239, 68, 68, 0.30); background: rgba(239, 68, 68, 0.06); }
|
||||
.dashboard-severity-insight-risk[data-level="severe"] .dashboard-severity-insight-risk-badge { background: rgba(239, 68, 68, 0.15); color: #dc2626; }
|
||||
|
||||
/* 风险分进度条 */
|
||||
.dashboard-severity-insight-score-track {
|
||||
width: 100%;
|
||||
height: 5px;
|
||||
border-radius: 999px;
|
||||
background: rgba(0, 0, 0, 0.06);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.dashboard-severity-insight-score-fill {
|
||||
height: 100%;
|
||||
border-radius: 999px;
|
||||
transition: width 0.4s ease, background 0.2s;
|
||||
background: #94a3b8;
|
||||
}
|
||||
|
||||
.dashboard-severity-insight-risk[data-level="safe"] .dashboard-severity-insight-score-fill { background: linear-gradient(90deg, #4ade80, #16a34a); }
|
||||
.dashboard-severity-insight-risk[data-level="low"] .dashboard-severity-insight-score-fill { background: linear-gradient(90deg, #60a5fa, #2563eb); }
|
||||
.dashboard-severity-insight-risk[data-level="medium"] .dashboard-severity-insight-score-fill { background: linear-gradient(90deg, #facc15, #ca8a04); }
|
||||
.dashboard-severity-insight-risk[data-level="high"] .dashboard-severity-insight-score-fill { background: linear-gradient(90deg, #fb923c, #ea580c); }
|
||||
.dashboard-severity-insight-risk[data-level="severe"] .dashboard-severity-insight-score-fill { background: linear-gradient(90deg, #f87171, #dc2626); }
|
||||
|
||||
.dashboard-severity-insight-score-meta {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
justify-content: space-between;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.dashboard-severity-insight-score-label {
|
||||
font-size: 0.6875rem;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.dashboard-severity-insight-score-value {
|
||||
font-size: 0.9375rem;
|
||||
font-weight: 800;
|
||||
color: var(--text-primary);
|
||||
font-variant-numeric: tabular-nums;
|
||||
letter-spacing: -0.02em;
|
||||
}
|
||||
|
||||
/* —— 待处理紧急项:分组(标题 + 两个小徽章) —— */
|
||||
.dashboard-severity-insight-urgent-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.dashboard-severity-insight-urgent {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.dashboard-severity-insight-urgent-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 2px;
|
||||
padding: 10px 6px;
|
||||
border-radius: 10px;
|
||||
background: #fafbfc;
|
||||
border: 1px solid rgba(0, 0, 0, 0.05);
|
||||
cursor: pointer;
|
||||
transition: background 0.15s, border-color 0.15s, transform 0.15s, box-shadow 0.15s;
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.dashboard-severity-insight-urgent-item:hover {
|
||||
transform: translateY(-1px);
|
||||
background: #fff;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
|
||||
.dashboard-severity-insight-urgent-item:focus-visible {
|
||||
outline: 2px solid rgba(0, 102, 255, 0.5);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.dashboard-severity-insight-urgent-item.u-critical { border-color: rgba(239, 68, 68, 0.22); }
|
||||
.dashboard-severity-insight-urgent-item.u-critical:hover { border-color: rgba(239, 68, 68, 0.40); }
|
||||
.dashboard-severity-insight-urgent-item.u-high { border-color: rgba(249, 115, 22, 0.22); }
|
||||
.dashboard-severity-insight-urgent-item.u-high:hover { border-color: rgba(249, 115, 22, 0.40); }
|
||||
|
||||
.dashboard-severity-insight-urgent-value {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 800;
|
||||
line-height: 1.1;
|
||||
font-variant-numeric: tabular-nums;
|
||||
letter-spacing: -0.02em;
|
||||
}
|
||||
|
||||
.dashboard-severity-insight-urgent-item.u-critical .dashboard-severity-insight-urgent-value { color: #dc2626; }
|
||||
.dashboard-severity-insight-urgent-item.u-high .dashboard-severity-insight-urgent-value { color: #ea580c; }
|
||||
|
||||
/* 当数量为 0 时,数值变灰,避免在无紧急项时仍然引人注目 */
|
||||
.dashboard-severity-insight-urgent-item.is-zero .dashboard-severity-insight-urgent-value {
|
||||
color: var(--text-secondary);
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.dashboard-severity-insight-urgent-label {
|
||||
font-size: 0.6875rem;
|
||||
color: var(--text-secondary);
|
||||
font-weight: 500;
|
||||
letter-spacing: 0.02em;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* —— 最近发现 —— */
|
||||
.dashboard-severity-insight-latest {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
padding: 2px 4px;
|
||||
}
|
||||
|
||||
.dashboard-severity-insight-time {
|
||||
font-size: 0.875rem;
|
||||
font-weight: 700;
|
||||
color: var(--text-primary);
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
.dashboard-severity-insight-time.is-empty {
|
||||
color: var(--text-secondary);
|
||||
font-weight: 500;
|
||||
opacity: 0.75;
|
||||
}
|
||||
|
||||
.dashboard-severity-chart {
|
||||
|
||||
@@ -95,6 +95,15 @@
|
||||
"severityLow": "Low",
|
||||
"severityInfo": "Info",
|
||||
"totalVulns": "Total vulnerabilities",
|
||||
"riskLevel": "Risk level",
|
||||
"riskScore": "Weighted risk score",
|
||||
"riskSafe": "Safe",
|
||||
"riskLow": "Low",
|
||||
"riskMedium": "Medium",
|
||||
"riskHigh": "High",
|
||||
"riskSevere": "Severe",
|
||||
"latestFound": "Latest found",
|
||||
"noneYet": "None yet",
|
||||
"runOverview": "Run overview",
|
||||
"batchQueues": "Batch task queues",
|
||||
"pending": "Pending",
|
||||
|
||||
@@ -95,6 +95,15 @@
|
||||
"severityLow": "低危",
|
||||
"severityInfo": "信息",
|
||||
"totalVulns": "总漏洞数",
|
||||
"riskLevel": "风险等级",
|
||||
"riskScore": "加权风险分",
|
||||
"riskSafe": "安全",
|
||||
"riskLow": "低",
|
||||
"riskMedium": "中",
|
||||
"riskHigh": "高",
|
||||
"riskSevere": "极高",
|
||||
"latestFound": "最近发现",
|
||||
"noneYet": "暂无",
|
||||
"runOverview": "运行概览",
|
||||
"batchQueues": "批量任务队列",
|
||||
"pending": "待执行",
|
||||
|
||||
@@ -46,6 +46,7 @@ async function refreshDashboard() {
|
||||
if (severityTotalEl) severityTotalEl.textContent = '0';
|
||||
renderSeverityDonut({}, 0);
|
||||
renderVulnStatusPanel(null, 0);
|
||||
renderSeverityInsights(null, 0, null);
|
||||
setDashboardOverviewPlaceholder('…');
|
||||
setEl('dashboard-kpi-tools-calls', '…');
|
||||
setEl('dashboard-kpi-success-rate', '…');
|
||||
@@ -91,15 +92,16 @@ async function refreshDashboard() {
|
||||
|
||||
try {
|
||||
// /api/vulnerabilities/stats 只给出 by_severity 与 by_status 两个独立维度,
|
||||
// 无法得到「严重 × 待处理」的交叉计数。这里额外拉两次(limit=1,仅取 total),
|
||||
// 用真实的「待处理严重 / 待处理高危」数量驱动告警条与 KPI 副标,避免修复后仍报警。
|
||||
// 无法得到「严重 × 待处理」的交叉计数。这里按四档各拉一次(limit=1,仅取 total),
|
||||
// 用真实的「待处理 × 各严重度」数量驱动告警条 / KPI 副标 / 风险概览卡的加权分,
|
||||
// 避免「全部修复后风险等级仍显示极高」这类语义冲突。
|
||||
var openVulnQuery = function (sev) {
|
||||
return fetchJson('/api/vulnerabilities?severity=' + sev + '&status=open&limit=1');
|
||||
};
|
||||
const [
|
||||
tasksRes, vulnRes, batchRes, monitorRes, knowledgeRes, skillsRes,
|
||||
recentVulnsRes, rolesRes, agentsRes,
|
||||
openCriticalRes, openHighRes, toolsConfigRes,
|
||||
openCriticalRes, openHighRes, openMediumRes, openLowRes, toolsConfigRes,
|
||||
hitlPendingRes, notificationsRes, externalMcpStatsRes,
|
||||
webshellRes
|
||||
] = await Promise.all([
|
||||
@@ -114,6 +116,9 @@ async function refreshDashboard() {
|
||||
fetchJson('/api/multi-agent/markdown-agents'),
|
||||
openVulnQuery('critical'),
|
||||
openVulnQuery('high'),
|
||||
// 中/低危的「待处理」计数:用于风险概览卡的加权风险分,使其反映"当前未处理风险"
|
||||
openVulnQuery('medium'),
|
||||
openVulnQuery('low'),
|
||||
// 拉取 MCP 工具的「配置总数」用于「能力总览」(区别于 monitor/stats 的「有调用记录」)。
|
||||
// 仅取 total 字段,page_size=1 减少传输;total 已涵盖内部 + 外部 MCP + 直接注册的工具。
|
||||
fetchJson('/api/config/tools?page=1&page_size=1'),
|
||||
@@ -173,17 +178,25 @@ async function refreshDashboard() {
|
||||
|
||||
let criticalCount = 0;
|
||||
let highCount = 0;
|
||||
let mediumCount = 0;
|
||||
let lowCount = 0;
|
||||
let openCriticalCount = 0;
|
||||
let openHighCount = 0;
|
||||
let openMediumCount = 0;
|
||||
let openLowCount = 0;
|
||||
if (vulnRes && typeof vulnRes.total === 'number') {
|
||||
if (vulnTotalEl) vulnTotalEl.textContent = String(vulnRes.total);
|
||||
const bySeverity = vulnRes.by_severity || {};
|
||||
const total = vulnRes.total || 0;
|
||||
criticalCount = bySeverity.critical || 0;
|
||||
highCount = bySeverity.high || 0;
|
||||
mediumCount = bySeverity.medium || 0;
|
||||
lowCount = bySeverity.low || 0;
|
||||
// 优先用专门拉的「待处理」计数;若专项接口失败,则退回 by_severity(宁可误报,不可漏报)
|
||||
openCriticalCount = pickOpenCount(openCriticalRes, criticalCount);
|
||||
openHighCount = pickOpenCount(openHighRes, highCount);
|
||||
openMediumCount = pickOpenCount(openMediumRes, mediumCount);
|
||||
openLowCount = pickOpenCount(openLowRes, lowCount);
|
||||
if (severityTotalEl) severityTotalEl.textContent = String(total);
|
||||
severityIds.forEach(sev => {
|
||||
const count = bySeverity[sev] || 0;
|
||||
@@ -197,6 +210,11 @@ async function refreshDashboard() {
|
||||
});
|
||||
renderSeverityDonut(bySeverity, total);
|
||||
renderVulnStatusPanel(vulnRes.by_status || {}, total);
|
||||
renderSeverityInsights(
|
||||
{ critical: openCriticalCount, high: openHighCount, medium: openMediumCount, low: openLowCount },
|
||||
openCriticalCount + openHighCount + openMediumCount + openLowCount,
|
||||
recentVulnsRes
|
||||
);
|
||||
|
||||
// 漏洞 KPI 副标:徽章/文案均使用「待处理」口径
|
||||
const critBadge = document.getElementById('dashboard-kpi-vuln-critical-badge');
|
||||
@@ -225,6 +243,7 @@ async function refreshDashboard() {
|
||||
});
|
||||
renderSeverityDonut({}, 0);
|
||||
renderVulnStatusPanel(null, 0);
|
||||
renderSeverityInsights(null, 0, null);
|
||||
hideEl('dashboard-kpi-vuln-critical-badge');
|
||||
setKpiSubText('dashboard-kpi-vuln-sub-text', '-');
|
||||
}
|
||||
@@ -1133,6 +1152,81 @@ function renderVulnStatusPanel(byStatus, total) {
|
||||
if (confirmedBar) confirmedBar.style.width = confirmedPct.toFixed(2) + '%';
|
||||
}
|
||||
|
||||
// 风险概览卡:基于「待处理(open)」口径的严重度分布计算加权风险分 + 紧急徽章
|
||||
//
|
||||
// 为什么用 open 口径而不是全量:
|
||||
// 如果用全量,全部漏洞修复后 by_severity 不变,风险分仍然居高,
|
||||
// 但紧急徽章(待严重/待高危)已经归零——视觉上会出现「极高 + 0 待处理」的语义冲突。
|
||||
// 改成 open 口径后,修复即卸掉风险,风险等级与紧急计数完全同步。
|
||||
//
|
||||
// bySeverityOpen: { critical, high, medium, low }(只统计 status=open 的漏洞;info 不计入)
|
||||
// totalOpen: 待处理漏洞总数(= critical + high + medium + low),仅用于"全无待处理 → safe"判断
|
||||
// recentVulnsRes: /api/vulnerabilities?limit=5 响应(用于"最近发现"时间,口径是全量,与处置状态无关)
|
||||
function renderSeverityInsights(bySeverityOpen, totalOpen, recentVulnsRes) {
|
||||
var riskBox = document.querySelector('.dashboard-severity-insight-risk');
|
||||
var levelEl = document.getElementById('dashboard-severity-risk-level');
|
||||
var fillEl = document.getElementById('dashboard-severity-risk-fill');
|
||||
var scoreEl = document.getElementById('dashboard-severity-risk-score');
|
||||
var urgentCriticalEl = document.getElementById('dashboard-severity-urgent-critical');
|
||||
var urgentHighEl = document.getElementById('dashboard-severity-urgent-high');
|
||||
var urgentCriticalCell = urgentCriticalEl ? urgentCriticalEl.closest('.dashboard-severity-insight-urgent-item') : null;
|
||||
var urgentHighCell = urgentHighEl ? urgentHighEl.closest('.dashboard-severity-insight-urgent-item') : null;
|
||||
var latestEl = document.getElementById('dashboard-severity-latest-time');
|
||||
|
||||
var sev = bySeverityOpen && typeof bySeverityOpen === 'object' ? bySeverityOpen : {};
|
||||
var c = Number(sev.critical || 0) || 0;
|
||||
var h = Number(sev.high || 0) || 0;
|
||||
var m = Number(sev.medium || 0) || 0;
|
||||
var l = Number(sev.low || 0) || 0;
|
||||
|
||||
// 加权分:严重 ×10、高危 ×5、中危 ×2、低危 ×0.5;信息忽略
|
||||
// 阈值设计偏"保守":1 个待处理严重就进"中",2 个进"高",≥4 个进"极高"
|
||||
var score = c * 10 + h * 5 + m * 2 + l * 0.5;
|
||||
var level, levelKey, levelFallback;
|
||||
var t = Number(totalOpen || 0) || 0;
|
||||
if (t === 0 || score === 0) {
|
||||
level = 'safe'; levelKey = 'dashboard.riskSafe'; levelFallback = '安全';
|
||||
} else if (score <= 3) {
|
||||
level = 'low'; levelKey = 'dashboard.riskLow'; levelFallback = '低';
|
||||
} else if (score <= 10) {
|
||||
level = 'medium'; levelKey = 'dashboard.riskMedium'; levelFallback = '中';
|
||||
} else if (score <= 30) {
|
||||
level = 'high'; levelKey = 'dashboard.riskHigh'; levelFallback = '高';
|
||||
} else {
|
||||
level = 'severe'; levelKey = 'dashboard.riskSevere'; levelFallback = '极高';
|
||||
}
|
||||
|
||||
if (riskBox) riskBox.setAttribute('data-level', level);
|
||||
if (levelEl) levelEl.textContent = dt(levelKey, null, levelFallback);
|
||||
// 进度条用 0-100 线性映射:>=100 直接满格
|
||||
var pct = Math.max(0, Math.min(100, score));
|
||||
if (fillEl) fillEl.style.width = pct.toFixed(1) + '%';
|
||||
if (scoreEl) {
|
||||
// 分数保留一位小数(低危 0.5 权重可能出现非整数);整数直接显示
|
||||
var displayScore = Math.round(score) === score ? String(score) : score.toFixed(1);
|
||||
scoreEl.textContent = score >= 100 ? displayScore + '+' : displayScore;
|
||||
}
|
||||
|
||||
// 紧急徽章直接复用 open 口径的 critical / high(与加权分完全同源,不会出现"风险极高 + 0 待处理"的矛盾)
|
||||
if (urgentCriticalEl) urgentCriticalEl.textContent = formatNumber(c);
|
||||
if (urgentHighEl) urgentHighEl.textContent = formatNumber(h);
|
||||
if (urgentCriticalCell) urgentCriticalCell.classList.toggle('is-zero', c === 0);
|
||||
if (urgentHighCell) urgentHighCell.classList.toggle('is-zero', h === 0);
|
||||
|
||||
if (latestEl) {
|
||||
var list = recentVulnsRes && Array.isArray(recentVulnsRes.vulnerabilities) ? recentVulnsRes.vulnerabilities : [];
|
||||
var latestIso = list.length > 0 ? list[0].created_at : null;
|
||||
var timeStr = latestIso ? timeAgoStr(latestIso) : '';
|
||||
if (timeStr) {
|
||||
latestEl.textContent = timeStr;
|
||||
latestEl.classList.remove('is-empty');
|
||||
} else {
|
||||
latestEl.textContent = dt('dashboard.noneYet', null, '暂无');
|
||||
latestEl.classList.add('is-empty');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function renderDashboardToolsBar(monitorRes) {
|
||||
const placeholder = document.getElementById('dashboard-tools-pie-placeholder');
|
||||
const barChartEl = document.getElementById('dashboard-tools-bar-chart');
|
||||
|
||||
@@ -388,6 +388,40 @@
|
||||
<a class="dashboard-section-link" onclick="switchPage('vulnerabilities')" data-i18n="dashboard.viewAll">查看全部 →</a>
|
||||
</div>
|
||||
<div class="dashboard-severity-wrap">
|
||||
<!-- 风险概览卡:填充 donut 左侧留白;提供「结论性」洞察(风险等级/加权分/待处理计数/最新时间),
|
||||
与右侧 legend 的「明细」形成互补,避免和下方「最近漏洞」列表重复 -->
|
||||
<aside class="dashboard-severity-insights" aria-label="风险概览">
|
||||
<div class="dashboard-severity-insight-risk" data-level="safe">
|
||||
<div class="dashboard-severity-insight-head">
|
||||
<span class="dashboard-severity-insight-label" data-i18n="dashboard.riskLevel">风险等级</span>
|
||||
<span class="dashboard-severity-insight-risk-badge" id="dashboard-severity-risk-level" data-i18n="dashboard.riskSafe">安全</span>
|
||||
</div>
|
||||
<div class="dashboard-severity-insight-score-track" aria-hidden="true">
|
||||
<div class="dashboard-severity-insight-score-fill" id="dashboard-severity-risk-fill" style="width: 0%"></div>
|
||||
</div>
|
||||
<div class="dashboard-severity-insight-score-meta">
|
||||
<span class="dashboard-severity-insight-score-label" data-i18n="dashboard.riskScore">加权风险分</span>
|
||||
<span class="dashboard-severity-insight-score-value" id="dashboard-severity-risk-score">0</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dashboard-severity-insight-urgent-group">
|
||||
<span class="dashboard-severity-insight-label" data-i18n="dashboard.statusOpen">待处理</span>
|
||||
<div class="dashboard-severity-insight-urgent">
|
||||
<div class="dashboard-severity-insight-urgent-item u-critical" role="button" tabindex="0" onclick="switchPage('vulnerabilities')" onkeydown="if(event.key==='Enter'||event.key===' ') { event.preventDefault(); switchPage('vulnerabilities'); }" title="查看待处理严重漏洞">
|
||||
<span class="dashboard-severity-insight-urgent-value" id="dashboard-severity-urgent-critical">0</span>
|
||||
<span class="dashboard-severity-insight-urgent-label" data-i18n="dashboard.severityCritical">严重</span>
|
||||
</div>
|
||||
<div class="dashboard-severity-insight-urgent-item u-high" role="button" tabindex="0" onclick="switchPage('vulnerabilities')" onkeydown="if(event.key==='Enter'||event.key===' ') { event.preventDefault(); switchPage('vulnerabilities'); }" title="查看待处理高危漏洞">
|
||||
<span class="dashboard-severity-insight-urgent-value" id="dashboard-severity-urgent-high">0</span>
|
||||
<span class="dashboard-severity-insight-urgent-label" data-i18n="dashboard.severityHigh">高危</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dashboard-severity-insight-latest">
|
||||
<span class="dashboard-severity-insight-label" data-i18n="dashboard.latestFound">最近发现</span>
|
||||
<span class="dashboard-severity-insight-time" id="dashboard-severity-latest-time" data-i18n="dashboard.noneYet">暂无</span>
|
||||
</div>
|
||||
</aside>
|
||||
<div class="dashboard-severity-chart">
|
||||
<svg class="dashboard-severity-donut" id="dashboard-severity-donut" viewBox="0 0 480 260" preserveAspectRatio="xMidYMid meet" aria-hidden="true">
|
||||
<g id="dashboard-severity-donut-track"></g>
|
||||
|
||||
Reference in New Issue
Block a user