Add files via upload

This commit is contained in:
公明
2026-05-31 13:33:32 +08:00
committed by GitHub
parent cbcbd414cd
commit eabfed09c9
5 changed files with 186 additions and 32 deletions
+71 -23
View File
@@ -21258,10 +21258,11 @@ button.chat-files-dropdown-item:hover:not(:disabled) {
border-radius: 14px;
box-shadow: 0 1px 3px rgba(15, 23, 42, 0.06);
overflow: hidden;
min-height: 420px;
min-height: 0;
align-self: stretch;
}
.projects-detail-header {
flex-shrink: 0;
display: flex;
align-items: flex-start;
justify-content: space-between;
@@ -21349,6 +21350,7 @@ button.chat-files-dropdown-item:hover:not(:disabled) {
padding: 12px 24px;
background: #f8fafc;
border-bottom: 1px solid #eef2f7;
flex-shrink: 0;
}
.projects-tab {
padding: 8px 16px;
@@ -21594,40 +21596,86 @@ button.chat-files-dropdown-item:hover:not(:disabled) {
overflow: hidden;
background: #fff;
}
#project-panel-facts {
#project-panel-facts,
#project-panel-conversations,
#project-panel-vulns {
display: flex;
flex-direction: column;
overflow: hidden;
min-height: 0;
}
#project-panel-facts .projects-fact-toolbar {
#project-panel-facts .projects-fact-toolbar,
#project-panel-vulns .projects-fact-toolbar,
#project-panel-conversations .projects-panel-toolbar {
flex: 0 0 auto;
}
#project-panel-facts .projects-table-wrap {
#project-panel-facts .projects-table-wrap,
#project-panel-conversations .projects-table-wrap,
#project-panel-vulns .projects-table-wrap {
flex: 1 1 auto;
min-height: 0;
max-height: 100%;
overflow: hidden;
}
#project-panel-facts .projects-table-wrap .data-table--projects {
width: 100%;
height: 100%;
table-layout: fixed;
border-spacing: 0;
}
#project-panel-facts .projects-table-wrap .data-table--projects thead,
#project-panel-facts .projects-table-wrap .data-table--projects tbody tr {
display: table;
width: 100%;
table-layout: fixed;
}
#project-panel-facts .projects-table-wrap .data-table--projects tbody {
display: block;
height: calc(100% - 42px);
overflow-y: auto;
overflow-x: hidden;
overflow-y: auto;
overscroll-behavior: contain;
-webkit-overflow-scrolling: touch;
}
#project-panel-facts .projects-table-wrap .data-table--projects thead th,
#project-panel-conversations .projects-table-wrap .data-table--projects thead th,
#project-panel-vulns .projects-table-wrap .data-table--projects thead th {
position: sticky;
top: 0;
z-index: 2;
box-shadow: 0 1px 0 var(--border-color, #e2e8f0);
}
.projects-panel-toolbar--hint {
margin-bottom: 14px;
padding: 0;
background: transparent;
border: none;
border-radius: 0;
}
.projects-panel-toolbar--hint .projects-fact-toolbar-hint {
margin: 0;
}
#project-panel-conversations .data-table--projects th:nth-child(1),
#project-panel-conversations .data-table--projects td:nth-child(1) {
width: 48%;
}
#project-panel-conversations .data-table--projects th:nth-child(2),
#project-panel-conversations .data-table--projects td:nth-child(2) {
width: 22%;
}
#project-panel-conversations .data-table--projects th:nth-child(3),
#project-panel-conversations .data-table--projects td:nth-child(3) {
width: 30%;
}
.projects-vuln-toolbar-top {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 12px;
flex-wrap: wrap;
}
.projects-vuln-toolbar-top .projects-fact-toolbar-hint {
flex: 1 1 240px;
margin: 0;
}
#project-panel-vulns .data-table--projects th:nth-child(1),
#project-panel-vulns .data-table--projects td:nth-child(1) {
width: 46%;
}
#project-panel-vulns .data-table--projects th:nth-child(2),
#project-panel-vulns .data-table--projects td:nth-child(2) {
width: 14%;
}
#project-panel-vulns .data-table--projects th:nth-child(3),
#project-panel-vulns .data-table--projects td:nth-child(3) {
width: 14%;
}
#project-panel-vulns .data-table--projects th:nth-child(4),
#project-panel-vulns .data-table--projects td:nth-child(4) {
width: 26%;
}
.projects-table-wrap .data-table--projects {
min-width: 0;
table-layout: fixed;
+3
View File
@@ -258,6 +258,9 @@
"boundConversationsHint": "Conversations bound to this project; click to open",
"titleLabel": "Title",
"projectVulnSummaryHint": "Vulnerability summary under this project",
"searchVulnsSr": "Search vulnerabilities",
"searchVulnsPlaceholder": "Search title, description, type, target…",
"noMatchingVulns": "No matching vulnerabilities, try adjusting filters",
"viewInVulnerabilityManagement": "View in vulnerability management",
"severity": "Severity",
"status": "Status",
+3
View File
@@ -247,6 +247,9 @@
"boundConversationsHint": "绑定到本项目的对话;点击可打开会话",
"titleLabel": "标题",
"projectVulnSummaryHint": "本项目下记录的漏洞汇总",
"searchVulnsSr": "搜索漏洞",
"searchVulnsPlaceholder": "搜索标题、描述、类型、目标…",
"noMatchingVulns": "无匹配漏洞,请调整筛选条件",
"viewInVulnerabilityManagement": "在漏洞管理中查看",
"severity": "严重度",
"status": "状态",
+59 -4
View File
@@ -336,6 +336,50 @@ function formatSeverityBadge(severity) {
return `<span class="projects-severity ${cls}">${escapeHtml(severity || '—')}</span>`;
}
function formatVulnStatusBadge(status) {
const s = (status || 'open').toLowerCase();
const labelMap = {
open: 'vulnerabilityPage.statusOpen',
confirmed: 'vulnerabilityPage.statusConfirmed',
fixed: 'vulnerabilityPage.statusFixed',
false_positive: 'vulnerabilityPage.statusFalsePositive',
};
const label = labelMap[s] ? tp(labelMap[s]) : status || '—';
const cls = ['open', 'confirmed', 'fixed', 'false_positive'].includes(s) ? s : 'open';
return `<span class="status-badge status-${escapeHtml(cls)}">${escapeHtml(label)}</span>`;
}
let _projectVulnsFilterDebounce = null;
function buildProjectVulnsQueryParams() {
const params = new URLSearchParams();
params.set('project_id', currentProjectId);
params.set('limit', '200');
const search = document.getElementById('project-vulns-search')?.value?.trim();
const severity = document.getElementById('project-vulns-filter-severity')?.value?.trim();
const status = document.getElementById('project-vulns-filter-status')?.value?.trim();
if (search) params.set('q', search);
if (severity) params.set('severity', severity);
if (status) params.set('status', status);
return params;
}
function projectVulnsHasActiveFilter() {
return !!(
document.getElementById('project-vulns-search')?.value?.trim() ||
document.getElementById('project-vulns-filter-severity')?.value ||
document.getElementById('project-vulns-filter-status')?.value
);
}
function debouncedLoadProjectVulnerabilities() {
if (_projectVulnsFilterDebounce) clearTimeout(_projectVulnsFilterDebounce);
_projectVulnsFilterDebounce = setTimeout(() => {
_projectVulnsFilterDebounce = null;
loadProjectVulnerabilities();
}, 280);
}
function getProjectsListFilter() {
return (document.getElementById('projects-list-search')?.value || '').trim().toLowerCase();
}
@@ -417,10 +461,16 @@ async function selectProject(id) {
const catEl = document.getElementById('project-facts-filter-category');
const confEl = document.getElementById('project-facts-filter-confidence');
const sparseEl = document.getElementById('project-facts-filter-sparse');
const vulnSearchEl = document.getElementById('project-vulns-search');
const vulnSevEl = document.getElementById('project-vulns-filter-severity');
const vulnStatusEl = document.getElementById('project-vulns-filter-status');
if (searchEl) searchEl.value = '';
if (catEl) catEl.value = '';
if (confEl) confEl.value = '';
if (sparseEl) sparseEl.checked = false;
if (vulnSearchEl) vulnSearchEl.value = '';
if (vulnSevEl) vulnSevEl.value = '';
if (vulnStatusEl) vulnStatusEl.value = '';
renderProjectsSidebar();
updateProjectsDetailVisibility();
try {
@@ -846,15 +896,18 @@ async function loadProjectVulnerabilities() {
const tbody = document.getElementById('project-vulns-tbody');
if (!tbody || !currentProjectId) return;
tbody.innerHTML = `<tr class="is-empty-row"><td colspan="4">${escapeHtml(tp('common.loading'))}</td></tr>`;
const res = await apiFetch(`/api/vulnerabilities?project_id=${encodeURIComponent(currentProjectId)}&limit=100`);
const qs = buildProjectVulnsQueryParams().toString();
const res = await apiFetch(`/api/vulnerabilities?${qs}`);
if (!res.ok) {
tbody.innerHTML = `<tr class="is-empty-row"><td colspan="4">${escapeHtml(tp('common.loadFailed'))}</td></tr>`;
return;
}
const data = await res.json();
const items = data.Vulnerabilities || data.vulnerabilities || data.items || [];
const items = data.Vulnerabilities || data.vulnerabilities || data.items || (Array.isArray(data) ? data : []);
if (!items.length) {
tbody.innerHTML = `<tr class="is-empty-row"><td colspan="4">${escapeHtml(tp('projects.noVulnerabilityRecords'))}</td></tr>`;
tbody.innerHTML = `<tr class="is-empty-row"><td colspan="4">${
projectVulnsHasActiveFilter() ? tp('projects.noMatchingVulns') : tp('projects.noVulnerabilityRecords')
}</td></tr>`;
refreshProjectHeaderStats();
return;
}
@@ -863,7 +916,7 @@ async function loadProjectVulnerabilities() {
return `<tr>
<td class="cell-summary" title="${escapeHtml(v.title)}">${escapeHtml(v.title)}</td>
<td>${formatSeverityBadge(v.severity)}</td>
<td>${escapeHtml(v.status)}</td>
<td>${formatVulnStatusBadge(v.status)}</td>
<td class="col-actions">
<div class="projects-table-actions">
<button type="button" class="projects-action-btn projects-action-btn--view" data-vuln-id="${idEsc}" onclick="openVulnerabilityDetail(this.dataset.vulnId)">${escapeHtml(tp('common.view'))}</button>
@@ -1542,6 +1595,8 @@ window.openVulnerabilitiesForProject = openVulnerabilitiesForProject;
window.openVulnerabilityDetail = openVulnerabilityDetail;
window.filterProjectsList = filterProjectsList;
window.debouncedLoadProjectFacts = debouncedLoadProjectFacts;
window.debouncedLoadProjectVulnerabilities = debouncedLoadProjectVulnerabilities;
window.loadProjectVulnerabilities = loadProjectVulnerabilities;
window.linkFactToExistingVulnerability = linkFactToExistingVulnerability;
window.createVulnerabilityFromCurrentFact = createVulnerabilityFromCurrentFact;
window.viewFactsForVulnerability = viewFactsForVulnerability;