From 92c0ae19bb63b3b9c80a28b9994fce99adc3109e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=85=AC=E6=98=8E?= <83812544+Ed1s0nZ@users.noreply.github.com> Date: Fri, 5 Jun 2026 10:35:41 +0800 Subject: [PATCH] Add files via upload --- web/static/css/style.css | 10 ++++++ web/static/i18n/en-US.json | 6 ++++ web/static/i18n/zh-CN.json | 6 ++++ web/static/js/vulnerability.js | 56 +++++++++++++++++++++++++++++++++- web/templates/index.html | 1 + 5 files changed, 78 insertions(+), 1 deletion(-) diff --git a/web/static/css/style.css b/web/static/css/style.css index 7b8ef752..33c25da0 100644 --- a/web/static/css/style.css +++ b/web/static/css/style.css @@ -12771,6 +12771,16 @@ tr.mcp-stats-tool-row[data-tool-name]:focus-visible { } /* 漏洞管理页面样式 */ +.page-header-actions .btn-secondary.btn-delete { + color: var(--error-color, #dc3545); +} + +.page-header-actions .btn-secondary.btn-delete:hover { + color: #c82333; + border-color: rgba(220, 53, 69, 0.35); + background: rgba(220, 53, 69, 0.06); +} + .vulnerability-dashboard { margin-bottom: 24px; } diff --git a/web/static/i18n/en-US.json b/web/static/i18n/en-US.json index 56585bcd..894eec8c 100644 --- a/web/static/i18n/en-US.json +++ b/web/static/i18n/en-US.json @@ -1787,6 +1787,12 @@ "loadListFailed": "Failed to load", "noRecords": "No vulnerability records", "batchExport": "Batch export", + "batchDelete": "Batch delete", + "batchDeleteNoResults": "No vulnerabilities match the current filters to delete", + "batchDeleteConfirm": "Delete {{count}} vulnerability record(s) matching the current filters? This cannot be undone.", + "batchDeleteConfirmAll": "No filters are set. This will delete all {{count}} vulnerability record(s). This cannot be undone. Continue?", + "batchDeleteSuccess": "Successfully deleted {{count}} vulnerability record(s)", + "batchDeleteFailed": "Batch delete failed", "downloadMarkdownTitle": "Download Markdown", "exportNoResults": "No vulnerabilities match the current filters", "exportStarted": "Started downloading {{count}} file(s)", diff --git a/web/static/i18n/zh-CN.json b/web/static/i18n/zh-CN.json index d2f26bc7..129da128 100644 --- a/web/static/i18n/zh-CN.json +++ b/web/static/i18n/zh-CN.json @@ -1776,6 +1776,12 @@ "loadListFailed": "加载失败", "noRecords": "暂无漏洞记录", "batchExport": "批量导出", + "batchDelete": "批量删除", + "batchDeleteNoResults": "当前筛选条件下没有可删除的漏洞", + "batchDeleteConfirm": "确定要删除当前筛选条件下的 {{count}} 条漏洞吗?此操作不可恢复。", + "batchDeleteConfirmAll": "未设置筛选条件,将删除全部 {{count}} 条漏洞。此操作不可恢复,确定继续?", + "batchDeleteSuccess": "成功删除 {{count}} 条漏洞", + "batchDeleteFailed": "批量删除失败", "downloadMarkdownTitle": "下载 Markdown", "exportNoResults": "当前筛选条件下无可导出漏洞", "exportStarted": "已开始下载 {{count}} 份报告", diff --git a/web/static/js/vulnerability.js b/web/static/js/vulnerability.js index 7b911986..576f23b0 100644 --- a/web/static/js/vulnerability.js +++ b/web/static/js/vulnerability.js @@ -720,7 +720,7 @@ async function loadVulnerabilityStats() { throw new Error('apiFetch未定义'); } - const params = buildVulnerabilityFilterParams(); + const params = buildVulnerabilityDashboardStatsParams(); const response = await apiFetch(`/api/vulnerabilities/stats?${params.toString()}`); if (!response.ok) { @@ -1531,6 +1531,13 @@ function buildVulnerabilityFilterParams() { return params; } +/** 看板统计:保留项目/关键词等筛选,但不带严重度(卡片本身用于切换严重度筛选) */ +function buildVulnerabilityDashboardStatsParams() { + const params = buildVulnerabilityFilterParams(); + params.delete('severity'); + return params; +} + function triggerTextDownload(fileName, content) { const blob = new Blob([content], { type: 'text/markdown;charset=utf-8' }); const url = URL.createObjectURL(blob); @@ -1543,6 +1550,53 @@ function triggerTextDownload(fileName, content) { URL.revokeObjectURL(url); } +function hasActiveVulnerabilityFilters() { + const keys = ['q', 'id', 'project_id', 'conversation_id', 'task_id', 'conversation_tag', 'task_tag', 'severity', 'status']; + return keys.some(function (k) { + return Boolean(vulnerabilityFilters[k]); + }); +} + +async function batchDeleteVulnerabilityReports() { + try { + const params = buildVulnerabilityFilterParams(); + const statsResponse = await apiFetch(`/api/vulnerabilities/stats?${params.toString()}`); + if (!statsResponse.ok) { + throw new Error(vulnT('vulnerabilityPage.deleteFailed')); + } + const stats = await statsResponse.json(); + const count = stats.total || 0; + if (count <= 0) { + alert(vulnT('vulnerabilityPage.batchDeleteNoResults')); + return; + } + + const confirmKey = hasActiveVulnerabilityFilters() + ? 'vulnerabilityPage.batchDeleteConfirm' + : 'vulnerabilityPage.batchDeleteConfirmAll'; + if (!confirm(vulnT(confirmKey, { count: count }))) { + return; + } + + const response = await apiFetch(`/api/vulnerabilities/batch?${params.toString()}`, { + method: 'DELETE' + }); + if (!response.ok) { + const error = await response.json().catch(() => ({ error: vulnT('vulnerabilityPage.deleteFailed') })); + throw new Error(error.error || vulnT('vulnerabilityPage.deleteFailed')); + } + const data = await response.json(); + const deleted = data.deleted || 0; + alert(vulnT('vulnerabilityPage.batchDeleteSuccess', { count: deleted })); + vulnerabilityPagination.currentPage = 1; + loadVulnerabilityStats(); + loadVulnerabilities(); + } catch (error) { + console.error('批量删除漏洞失败:', error); + alert(vulnT('vulnerabilityPage.batchDeleteFailed') + ': ' + error.message); + } +} + async function exportVulnerabilityReports() { try { const params = buildVulnerabilityFilterParams(); diff --git a/web/templates/index.html b/web/templates/index.html index 400014f3..dfbe7e03 100644 --- a/web/templates/index.html +++ b/web/templates/index.html @@ -1714,6 +1714,7 @@

漏洞管理

+