diff --git a/web/static/css/style.css b/web/static/css/style.css index c10635e2..91051e89 100644 --- a/web/static/css/style.css +++ b/web/static/css/style.css @@ -21359,6 +21359,11 @@ button.chat-files-dropdown-item:hover:not(:disabled) { color: #b45309; background: #fffbeb; } +.projects-action-btn--restore:hover { + border-color: #86efac; + color: #15803d; + background: #f0fdf4; +} .projects-action-btn--danger { color: #dc2626; border-color: #fecaca; diff --git a/web/static/js/projects.js b/web/static/js/projects.js index c1c95b18..ce1c756f 100644 --- a/web/static/js/projects.js +++ b/web/static/js/projects.js @@ -286,11 +286,15 @@ function formatConfidenceBadge(confidence) { return `${escapeHtml(label)}`; } -function renderProjectFactActions(keyEsc, idEsc) { +function renderProjectFactActions(keyEsc, idEsc, confidence) { + const isDeprecated = (confidence || '').toLowerCase() === 'deprecated'; + const toggleBtn = isDeprecated + ? `` + : ``; return `
- + ${toggleBtn}
`; } @@ -434,7 +438,7 @@ async function loadProjectFacts() { ${formatFactBodyBadge(f)} ${formatConfidenceBadge(f.confidence)} ${formatProjectTime(f.updated_at, f.created_at)} - ${renderProjectFactActions(keyEsc, idEsc)} + ${renderProjectFactActions(keyEsc, idEsc, f.confidence)} `; }).join(''); refreshProjectHeaderStats(); @@ -507,7 +511,7 @@ function closeFactDetailModal() { } async function deprecateProjectFactByKey(factKey) { - if (!confirm(`将事实 ${factKey} 标记为 deprecated?`)) return; + if (!confirm(`将事实 ${factKey} 标记为已废弃?`)) return; const res = await apiFetch(`/api/projects/${currentProjectId}/facts/deprecate`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, @@ -517,6 +521,20 @@ async function deprecateProjectFactByKey(factKey) { loadProjectFacts(); } +async function restoreProjectFactByKey(factKey) { + if (!confirm(`恢复事实 ${factKey}?将重新进入黑板索引(状态:待确认)。`)) return; + const res = await apiFetch(`/api/projects/${currentProjectId}/facts/restore`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ fact_key: factKey, confidence: 'tentative' }), + }); + if (!res.ok) { + const err = await res.json().catch(() => ({})); + return alert(err.error || '操作失败'); + } + loadProjectFacts(); +} + function openVulnerabilitiesForProject(projectId) { const pid = projectId || currentProjectId; if (!pid) return; @@ -1131,6 +1149,7 @@ window.viewProjectFactBody = viewProjectFactBody; window.insertFactBodyTemplate = insertFactBodyTemplate; window.updateFactFormHints = updateFactFormHints; window.deprecateProjectFactByKey = deprecateProjectFactByKey; +window.restoreProjectFactByKey = restoreProjectFactByKey; window.openVulnerabilitiesForProject = openVulnerabilitiesForProject; window.openVulnerabilityDetail = openVulnerabilityDetail; window.filterProjectsList = filterProjectsList;