Add files via upload

This commit is contained in:
公明
2026-05-22 11:28:51 +08:00
committed by GitHub
parent 0358d3a67d
commit c446e22d0c
5 changed files with 358 additions and 193 deletions
+134 -83
View File
@@ -16990,8 +16990,8 @@ header {
}
.vulnerability-controls {
margin-bottom: 16px;
padding: 10px 12px;
margin-bottom: 12px;
padding: 8px 10px;
background: var(--bg-primary);
border: 1px solid var(--border-color);
border-radius: 10px;
@@ -17016,6 +17016,117 @@ header {
min-width: 0;
}
.vulnerability-filter-actions {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 6px;
flex-shrink: 0;
}
.vulnerability-search-wrap {
position: relative;
gap: 0;
}
.vulnerability-search-wrap .vulnerability-search-icon {
position: absolute;
left: 10px;
top: 50%;
transform: translateY(-50%);
color: var(--text-secondary);
pointer-events: none;
flex-shrink: 0;
}
.vulnerability-search-wrap input[type="search"] {
padding-left: 32px;
}
.vulnerability-more-filters-anchor {
position: relative;
}
.vulnerability-more-filters-btn {
display: inline-flex;
align-items: center;
gap: 4px;
white-space: nowrap;
padding: 8px 12px;
font-size: 0.875rem;
}
.vulnerability-more-filters-btn.is-active {
border-color: #3b82f6;
color: #2563eb;
background: rgba(59, 130, 246, 0.06);
}
.vulnerability-more-filters-popover {
position: absolute;
top: calc(100% + 6px);
right: 0;
z-index: 120;
width: min(420px, calc(100vw - 32px));
padding: 12px;
background: var(--bg-primary);
border: 1px solid var(--border-color);
border-radius: 10px;
box-shadow: var(--shadow-md, 0 8px 24px rgba(15, 23, 42, 0.12));
}
.vulnerability-more-filters-popover[hidden] {
display: none !important;
}
.vulnerability-more-filters-popover-title {
margin: 0 0 10px;
font-size: 0.8125rem;
font-weight: 600;
color: var(--text-secondary);
}
.vulnerability-more-filters-popover-body {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 10px 12px;
}
.vulnerability-more-filters-popover-body .vulnerability-filter-field--full {
grid-column: 1 / -1;
}
.vulnerability-more-filters-popover-body .vulnerability-filter-field {
flex-direction: column;
align-items: stretch;
gap: 4px;
}
.vulnerability-more-filters-popover-body .vulnerability-filter-field > span {
font-size: 0.75rem;
color: var(--text-secondary);
}
.vulnerability-more-filters-popover-footer {
display: flex;
justify-content: flex-end;
gap: 8px;
margin-top: 12px;
padding-top: 10px;
border-top: 1px solid var(--border-color);
}
@media (max-width: 520px) {
.vulnerability-more-filters-popover-body {
grid-template-columns: 1fr;
}
.vulnerability-more-filters-popover {
right: auto;
left: 0;
}
}
.vulnerability-filter-field {
display: flex;
align-items: center;
@@ -17061,6 +17172,10 @@ header {
box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.12);
}
.vulnerability-filter-clear-btn[hidden] {
display: none !important;
}
.vulnerability-filter-clear-btn {
flex-shrink: 0;
padding: 8px 12px;
@@ -17078,69 +17193,7 @@ header {
background: rgba(59, 130, 246, 0.06);
}
/* tasks-filters 的 display:flex 会覆盖 [hidden],必须显式隐藏 */
#vulnerability-advanced-filters[hidden] {
display: none !important;
}
.vulnerability-filter-advanced-wrap {
margin-top: 6px;
}
.vulnerability-filter-advanced-wrap.is-expanded {
margin-top: 10px;
padding-top: 10px;
border-top: 1px dashed var(--border-color);
}
.vulnerability-filter-advanced {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 10px 14px;
}
.vulnerability-filter-advanced .vulnerability-filter-field {
flex-direction: column;
align-items: stretch;
gap: 4px;
}
.vulnerability-filter-advanced .vulnerability-filter-field > span {
max-width: none;
font-size: 0.75rem;
}
.vulnerability-filter-advanced .vulnerability-filter-field input {
width: 100%;
}
@media (max-width: 768px) {
.vulnerability-filter-advanced {
grid-template-columns: 1fr;
}
}
.vulnerability-filter-advanced-toggle {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 4px 8px;
margin: 0 0 8px;
border: none;
background: transparent;
color: var(--text-secondary);
font-size: 0.8125rem;
cursor: pointer;
border-radius: 6px;
transition: color 0.15s ease, background 0.15s ease;
}
.vulnerability-filter-advanced-toggle:hover {
color: var(--text-primary);
background: rgba(0, 0, 0, 0.04);
}
/* 高级筛选生效数量:弱提示文字,避免实心蓝点过于抢眼 */
/* 更多筛选生效数量:弱提示文字 */
.vulnerability-filter-advanced-badge {
display: inline;
margin-left: 2px;
@@ -17155,30 +17208,23 @@ header {
display: none !important;
}
.vulnerability-filter-advanced-chevron {
display: inline-block;
width: 0;
height: 0;
border-left: 4px solid transparent;
border-right: 4px solid transparent;
border-top: 5px solid currentColor;
transition: transform 0.2s ease;
}
.vulnerability-filter-advanced-toggle[aria-expanded="true"] .vulnerability-filter-advanced-chevron {
transform: rotate(180deg);
}
.vulnerability-filter-chips {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 6px;
margin-top: 10px;
padding-top: 10px;
gap: 6px 8px;
margin-top: 8px;
padding-top: 8px;
border-top: 1px solid var(--border-color);
}
.vulnerability-filter-chips-label {
flex-shrink: 0;
font-size: 0.75rem;
font-weight: 600;
color: var(--text-secondary);
}
.vulnerability-filter-chips-list {
display: flex;
flex-wrap: wrap;
@@ -17222,8 +17268,13 @@ header {
flex: 1 1 100%;
}
.vulnerability-filter-actions {
width: 100%;
justify-content: flex-end;
}
.vulnerability-filter-clear-btn {
align-self: flex-end;
margin-left: auto;
}
}
+8
View File
@@ -1545,6 +1545,10 @@
"statClickAll": "View all (clear severity filter)",
"statClickFilter": "Click to filter by this severity; click again to clear",
"advancedFilters": "Advanced filters",
"moreFilters": "More filters",
"applyFilters": "Apply",
"clearAdvanced": "Clear",
"clearAll": "Reset all",
"activeFilters": "Active filters",
"chipRemove": "Remove",
"filter": "Filter",
@@ -1564,6 +1568,10 @@
"statusFixed": "Fixed",
"statusFalsePositive": "False positive",
"searchVulnId": "Search vuln ID",
"searchKeyword": "Search title, description, type, target…",
"searchKeywordShort": "Keyword",
"filterExactId": "Exact vuln ID",
"filterEnterHint": "Press Enter to filter",
"filterConversation": "Filter by conversation",
"loading": "Loading...",
"loadListFailed": "Failed to load",
+9 -1
View File
@@ -1534,6 +1534,10 @@
"statClickAll": "查看全部(清除严重度筛选)",
"statClickFilter": "点击按此严重度筛选;再次点击清除",
"advancedFilters": "高级筛选",
"moreFilters": "更多筛选",
"applyFilters": "应用",
"clearAdvanced": "清空",
"clearAll": "重置全部",
"activeFilters": "已选条件",
"chipRemove": "移除",
"filter": "筛选",
@@ -1552,7 +1556,11 @@
"statusConfirmed": "已确认",
"statusFixed": "已修复",
"statusFalsePositive": "误报",
"searchVulnId": "搜索漏洞ID",
"searchVulnId": "搜索漏洞 ID",
"searchKeyword": "搜索标题、描述、类型、目标…",
"searchKeywordShort": "关键词",
"filterExactId": "精确匹配漏洞 ID",
"filterEnterHint": "回车筛选",
"filterConversation": "筛选特定会话",
"loading": "加载中...",
"loadListFailed": "加载失败",
+152 -77
View File
@@ -46,6 +46,7 @@ const getVulnerabilityPageSize = () => {
let currentVulnerabilityId = null;
let vulnerabilityFilters = {
q: '',
id: '',
conversation_id: '',
task_id: '',
@@ -65,11 +66,14 @@ const VULN_STAT_SEVERITIES = ['critical', 'high', 'medium', 'low', 'info'];
let vulnerabilityStatCardsBound = false;
let vulnerabilityFilterPanelBound = false;
let vulnerabilityFilterOptionsCache = null;
const VULNERABILITY_ADVANCED_OPEN_KEY = 'vulnerabilityAdvancedFiltersOpen';
let vulnerabilityMoreFiltersPopoverOpen = false;
let vulnerabilityFilterDebounceTimer = null;
const VULNERABILITY_FILTER_DEBOUNCE_MS = 400;
const VULNERABILITY_DATALIST_MAX = 8;
const VULNERABILITY_DATALIST_MIN_QUERY = 2;
const VULN_FILTER_CHIP_FIELDS = [
{ key: 'q', labelKey: 'vulnerabilityPage.searchKeywordShort' },
{ key: 'id', labelKey: 'vulnerabilityPage.vulnId' },
{ key: 'status', labelKey: null, format: 'status' },
{ key: 'severity', labelKey: null, format: 'severity' },
@@ -94,10 +98,12 @@ function syncVulnerabilityFiltersFromLocationHash() {
const st = (params.get('status') || '').trim();
const convTag = (params.get('conversation_tag') || '').trim();
const taskTag = (params.get('task_tag') || '').trim();
if (!vid && !cid && !tid && !sev && !st && !convTag && !taskTag) {
const q = (params.get('q') || params.get('search') || '').trim();
if (!vid && !cid && !tid && !sev && !st && !convTag && !taskTag && !q) {
return;
}
vulnerabilityFilters.q = '';
vulnerabilityFilters.id = '';
vulnerabilityFilters.conversation_id = '';
vulnerabilityFilters.task_id = '';
@@ -105,14 +111,16 @@ function syncVulnerabilityFiltersFromLocationHash() {
vulnerabilityFilters.task_tag = '';
vulnerabilityFilters.severity = '';
vulnerabilityFilters.status = '';
const idEl = document.getElementById('vulnerability-id-filter');
const searchEl = document.getElementById('vulnerability-search-filter');
const exactIdEl = document.getElementById('vulnerability-exact-id-filter');
const convEl = document.getElementById('vulnerability-conversation-filter');
const taskEl = document.getElementById('vulnerability-task-filter');
const convTagEl = document.getElementById('vulnerability-conversation-tag-filter');
const taskTagEl = document.getElementById('vulnerability-task-tag-filter');
const sevEl = document.getElementById('vulnerability-severity-filter');
const stEl = document.getElementById('vulnerability-status-filter');
if (idEl) idEl.value = '';
if (searchEl) searchEl.value = '';
if (exactIdEl) exactIdEl.value = '';
if (convEl) convEl.value = '';
if (taskEl) taskEl.value = '';
if (convTagEl) convTagEl.value = '';
@@ -120,9 +128,13 @@ function syncVulnerabilityFiltersFromLocationHash() {
if (sevEl) sevEl.value = '';
if (stEl) stEl.value = '';
if (q) {
vulnerabilityFilters.q = q;
if (searchEl) searchEl.value = q;
}
if (vid) {
vulnerabilityFilters.id = vid;
if (idEl) idEl.value = vid;
if (exactIdEl) exactIdEl.value = vid;
}
if (cid) {
vulnerabilityFilters.conversation_id = cid;
@@ -149,9 +161,6 @@ function syncVulnerabilityFiltersFromLocationHash() {
if (stEl) stEl.value = st;
}
vulnerabilityPagination.currentPage = 1;
if (hasVulnerabilityAdvancedFiltersActive()) {
setVulnerabilityAdvancedFiltersOpen(true, false);
}
syncVulnerabilityStatCardActiveState();
updateVulnerabilityFilterPanelState();
renderVulnerabilityFilterChips();
@@ -213,7 +222,8 @@ function applyVulnerabilitySeverityFilter(severity) {
}
function readVulnerabilityFiltersFromForm() {
vulnerabilityFilters.id = (document.getElementById('vulnerability-id-filter')?.value || '').trim();
vulnerabilityFilters.q = (document.getElementById('vulnerability-search-filter')?.value || '').trim();
vulnerabilityFilters.id = (document.getElementById('vulnerability-exact-id-filter')?.value || '').trim();
vulnerabilityFilters.conversation_id = (document.getElementById('vulnerability-conversation-filter')?.value || '').trim();
vulnerabilityFilters.task_id = (document.getElementById('vulnerability-task-filter')?.value || '').trim();
vulnerabilityFilters.conversation_tag = (document.getElementById('vulnerability-conversation-tag-filter')?.value || '').trim();
@@ -225,13 +235,13 @@ function readVulnerabilityFiltersFromForm() {
function hasVulnerabilityAdvancedFiltersActive() {
const f = vulnerabilityFilters;
return Boolean(f.conversation_id || f.task_id || f.conversation_tag || f.task_tag);
return Boolean(f.id || f.conversation_id || f.task_id || f.conversation_tag || f.task_tag);
}
function hasAnyVulnerabilityFilterActive() {
const f = vulnerabilityFilters;
return Boolean(
f.id || f.conversation_id || f.task_id || f.conversation_tag || f.task_tag || f.severity || f.status
f.q || f.id || f.conversation_id || f.task_id || f.conversation_tag || f.task_tag || f.severity || f.status
);
}
@@ -253,6 +263,7 @@ function updateVulnerabilityLocationHashFromFilters() {
const params = new URLSearchParams(hashParts.length >= 2 ? hashParts.slice(1).join('?') : '');
const f = vulnerabilityFilters;
const pairs = [
['q', f.q],
['id', f.id],
['conversation_id', f.conversation_id],
['task_id', f.task_id],
@@ -279,17 +290,82 @@ function updateVulnerabilityLocationHashFromFilters() {
}
}
function toggleVulnerabilityAdvancedFilters(ev) {
function scheduleVulnerabilityFilterApply(immediate) {
if (vulnerabilityFilterDebounceTimer) {
clearTimeout(vulnerabilityFilterDebounceTimer);
vulnerabilityFilterDebounceTimer = null;
}
if (immediate) {
applyVulnerabilityFilters();
return;
}
vulnerabilityFilterDebounceTimer = setTimeout(function () {
vulnerabilityFilterDebounceTimer = null;
applyVulnerabilityFilters();
}, VULNERABILITY_FILTER_DEBOUNCE_MS);
}
function syncVulnerabilityAdvancedFieldsFromFilters() {
const map = {
id: 'vulnerability-exact-id-filter',
conversation_id: 'vulnerability-conversation-filter',
task_id: 'vulnerability-task-filter',
conversation_tag: 'vulnerability-conversation-tag-filter',
task_tag: 'vulnerability-task-tag-filter'
};
Object.keys(map).forEach(function (key) {
const el = document.getElementById(map[key]);
if (el) el.value = vulnerabilityFilters[key] || '';
});
}
function setVulnerabilityMoreFiltersPopoverOpen(open) {
const btn = document.getElementById('vulnerability-more-filters-btn');
const popover = document.getElementById('vulnerability-more-filters-popover');
if (!btn || !popover) return;
vulnerabilityMoreFiltersPopoverOpen = open;
btn.setAttribute('aria-expanded', open ? 'true' : 'false');
btn.classList.toggle('is-active', open);
popover.hidden = !open;
}
function toggleVulnerabilityMoreFiltersPopover(ev) {
if (ev) {
ev.preventDefault();
ev.stopPropagation();
}
const toggleBtn = document.getElementById('vulnerability-advanced-toggle');
if (!toggleBtn) return;
const expanded = toggleBtn.getAttribute('aria-expanded') === 'true';
setVulnerabilityAdvancedFiltersOpen(!expanded, true);
const opening = !vulnerabilityMoreFiltersPopoverOpen;
if (opening) {
readVulnerabilityFiltersFromForm();
syncVulnerabilityAdvancedFieldsFromFilters();
}
setVulnerabilityMoreFiltersPopoverOpen(opening);
}
function closeVulnerabilityMoreFiltersPopover(revertDraft) {
if (revertDraft) syncVulnerabilityAdvancedFieldsFromFilters();
setVulnerabilityMoreFiltersPopoverOpen(false);
}
function clearVulnerabilityAdvancedFilterFields() {
['vulnerability-exact-id-filter', 'vulnerability-conversation-filter', 'vulnerability-task-filter',
'vulnerability-conversation-tag-filter', 'vulnerability-task-tag-filter'].forEach(function (id) {
const el = document.getElementById(id);
if (el) el.value = '';
});
}
function applyVulnerabilityMoreFiltersFromPopover() {
closeVulnerabilityMoreFiltersPopover(false);
scheduleVulnerabilityFilterApply(true);
}
function onVulnerabilityFilterDocumentPointerDown(ev) {
if (!vulnerabilityMoreFiltersPopoverOpen) return;
const anchor = document.querySelector('.vulnerability-more-filters-anchor');
if (anchor && anchor.contains(ev.target)) return;
closeVulnerabilityMoreFiltersPopover(true);
}
window.toggleVulnerabilityAdvancedFilters = toggleVulnerabilityAdvancedFilters;
function initVulnerabilityFilterPanel() {
const panel = document.getElementById('vulnerability-filter-panel');
@@ -301,55 +377,69 @@ function initVulnerabilityFilterPanel() {
}
vulnerabilityFilterPanelBound = true;
let savedOpen = false;
try {
savedOpen = localStorage.getItem(VULNERABILITY_ADVANCED_OPEN_KEY) === 'true';
} catch (e) { /* ignore */ }
setVulnerabilityAdvancedFiltersOpen(savedOpen, false);
const stEl = document.getElementById('vulnerability-status-filter');
if (stEl) stEl.addEventListener('change', applyVulnerabilityFilters);
if (stEl) stEl.addEventListener('change', function () { scheduleVulnerabilityFilterApply(true); });
const textIds = [
'vulnerability-id-filter',
const searchEl = document.getElementById('vulnerability-search-filter');
if (searchEl) {
searchEl.addEventListener('input', function () { scheduleVulnerabilityFilterApply(false); });
searchEl.addEventListener('keydown', function (ev) {
if (ev.key === 'Enter') {
ev.preventDefault();
scheduleVulnerabilityFilterApply(true);
}
});
searchEl.addEventListener('search', function () {
if (searchEl.value === '') scheduleVulnerabilityFilterApply(true);
});
}
const moreBtn = document.getElementById('vulnerability-more-filters-btn');
if (moreBtn) moreBtn.addEventListener('click', toggleVulnerabilityMoreFiltersPopover);
const applyBtn = document.getElementById('vulnerability-more-filters-apply');
if (applyBtn) applyBtn.addEventListener('click', applyVulnerabilityMoreFiltersFromPopover);
const resetBtn = document.getElementById('vulnerability-more-filters-reset');
if (resetBtn) {
resetBtn.addEventListener('click', function () {
clearVulnerabilityAdvancedFilterFields();
applyVulnerabilityMoreFiltersFromPopover();
});
}
const advancedTextIds = [
'vulnerability-exact-id-filter',
'vulnerability-conversation-filter',
'vulnerability-task-filter',
'vulnerability-conversation-tag-filter',
'vulnerability-task-tag-filter'
];
textIds.forEach(function (id) {
advancedTextIds.forEach(function (id) {
const el = document.getElementById(id);
if (!el) return;
el.addEventListener('keydown', function (ev) {
if (ev.key === 'Enter') {
ev.preventDefault();
applyVulnerabilityFilters();
applyVulnerabilityMoreFiltersFromPopover();
}
});
});
document.addEventListener('mousedown', onVulnerabilityFilterDocumentPointerDown);
document.addEventListener('keydown', function (ev) {
if (ev.key === 'Escape' && vulnerabilityMoreFiltersPopoverOpen) {
closeVulnerabilityMoreFiltersPopover(true);
}
});
bindVulnerabilityFilterTypeaheads();
}
function setVulnerabilityAdvancedFiltersOpen(open, persist) {
const toggleBtn = document.getElementById('vulnerability-advanced-toggle');
const advanced = document.getElementById('vulnerability-advanced-filters');
const wrap = document.querySelector('#vulnerability-filter-panel .vulnerability-filter-advanced-wrap');
if (!toggleBtn || !advanced) return;
toggleBtn.setAttribute('aria-expanded', open ? 'true' : 'false');
advanced.hidden = !open;
advanced.classList.toggle('is-open', open);
if (wrap) wrap.classList.toggle('is-expanded', open);
if (persist) {
try {
localStorage.setItem(VULNERABILITY_ADVANCED_OPEN_KEY, open ? 'true' : 'false');
} catch (e) { /* ignore */ }
}
}
function countVulnerabilityAdvancedFiltersActive() {
const f = vulnerabilityFilters;
let n = 0;
if (f.id) n++;
if (f.conversation_id) n++;
if (f.task_id) n++;
if (f.conversation_tag) n++;
@@ -379,6 +469,8 @@ function updateVulnerabilityFilterPanelState() {
readVulnerabilityFiltersFromForm();
panel.classList.toggle('is-filtered', hasAnyVulnerabilityFilterActive());
updateVulnerabilityAdvancedBadge();
const clearBtn = document.getElementById('vulnerability-filter-clear-btn');
if (clearBtn) clearBtn.hidden = !hasAnyVulnerabilityFilterActive();
}
function formatVulnerabilityFilterChipValue(key, value) {
@@ -431,7 +523,8 @@ function renderVulnerabilityFilterChips() {
function removeVulnerabilityFilterByKey(key) {
const map = {
id: 'vulnerability-id-filter',
q: 'vulnerability-search-filter',
id: 'vulnerability-exact-id-filter',
conversation_id: 'vulnerability-conversation-filter',
task_id: 'vulnerability-task-filter',
conversation_tag: 'vulnerability-conversation-tag-filter',
@@ -609,13 +702,7 @@ async function loadVulnerabilityStats() {
throw new Error('apiFetch未定义');
}
const params = new URLSearchParams();
if (vulnerabilityFilters.conversation_id) {
params.append('conversation_id', vulnerabilityFilters.conversation_id);
}
if (vulnerabilityFilters.task_id) {
params.append('task_id', vulnerabilityFilters.task_id);
}
const params = buildVulnerabilityFilterParams();
const response = await apiFetch(`/api/vulnerabilities/stats?${params.toString()}`);
if (!response.ok) {
@@ -689,31 +776,9 @@ async function loadVulnerabilities(page = null) {
vulnerabilityPagination.currentPage = page;
}
const params = new URLSearchParams();
const params = buildVulnerabilityFilterParams();
params.append('page', vulnerabilityPagination.currentPage.toString());
params.append('limit', vulnerabilityPagination.pageSize.toString());
if (vulnerabilityFilters.id) {
params.append('id', vulnerabilityFilters.id);
}
if (vulnerabilityFilters.conversation_id) {
params.append('conversation_id', vulnerabilityFilters.conversation_id);
}
if (vulnerabilityFilters.task_id) {
params.append('task_id', vulnerabilityFilters.task_id);
}
if (vulnerabilityFilters.conversation_tag) {
params.append('conversation_tag', vulnerabilityFilters.conversation_tag);
}
if (vulnerabilityFilters.task_tag) {
params.append('task_tag', vulnerabilityFilters.task_tag);
}
if (vulnerabilityFilters.severity) {
params.append('severity', vulnerabilityFilters.severity);
}
if (vulnerabilityFilters.status) {
params.append('status', vulnerabilityFilters.status);
}
const response = await apiFetch(`/api/vulnerabilities?${params.toString()}`);
if (!response.ok) {
@@ -1090,8 +1155,14 @@ function filterVulnerabilities() {
// 清除筛选
function clearVulnerabilityFilters() {
closeVulnerabilityMoreFiltersPopover(false);
if (vulnerabilityFilterDebounceTimer) {
clearTimeout(vulnerabilityFilterDebounceTimer);
vulnerabilityFilterDebounceTimer = null;
}
const fields = [
'vulnerability-id-filter',
'vulnerability-search-filter',
'vulnerability-exact-id-filter',
'vulnerability-conversation-filter',
'vulnerability-task-filter',
'vulnerability-conversation-tag-filter',
@@ -1105,6 +1176,7 @@ function clearVulnerabilityFilters() {
});
vulnerabilityFilters = {
q: '',
id: '',
conversation_id: '',
task_id: '',
@@ -1277,8 +1349,11 @@ function formatVulnerabilityAsMarkdown(vuln) {
function buildVulnerabilityFilterParams() {
const params = new URLSearchParams();
if (vulnerabilityFilters.q) {
params.append('q', vulnerabilityFilters.q);
}
const keys = ['id', 'conversation_id', 'task_id', 'conversation_tag', 'task_tag', 'severity', 'status'];
keys.forEach((k) => {
keys.forEach(function (k) {
if (vulnerabilityFilters[k]) {
params.append(k, vulnerabilityFilters[k]);
}
+55 -32
View File
@@ -1446,10 +1446,14 @@
<div class="vulnerability-controls" id="vulnerability-filter-panel">
<div class="vulnerability-filter-toolbar">
<div class="vulnerability-filter-primary">
<label class="vulnerability-filter-field vulnerability-filter-field--grow">
<span class="sr-only" data-i18n="vulnerabilityPage.vulnId">漏洞ID</span>
<input type="search" id="vulnerability-id-filter" autocomplete="off"
data-i18n="vulnerabilityPage.searchVulnId" data-i18n-attr="placeholder" placeholder="搜索漏洞 ID,回车筛选" />
<label class="vulnerability-filter-field vulnerability-filter-field--grow vulnerability-search-wrap">
<span class="sr-only" data-i18n="vulnerabilityPage.searchKeyword">关键词搜索</span>
<svg class="vulnerability-search-icon" width="16" height="16" viewBox="0 0 24 24" fill="none" aria-hidden="true" xmlns="http://www.w3.org/2000/svg">
<circle cx="11" cy="11" r="7" stroke="currentColor" stroke-width="2"/>
<path d="M20 20L16 16" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
</svg>
<input type="search" id="vulnerability-search-filter" autocomplete="off"
data-i18n="vulnerabilityPage.searchKeyword" data-i18n-attr="placeholder" placeholder="搜索标题、描述、类型、目标…" />
</label>
<label class="vulnerability-filter-field vulnerability-filter-field--status">
<span class="sr-only" data-i18n="vulnerabilityPage.status">状态</span>
@@ -1461,7 +1465,52 @@
<option value="false_positive" data-i18n="vulnerabilityPage.statusFalsePositive">误报</option>
</select>
</label>
<button type="button" class="vulnerability-filter-clear-btn" onclick="clearVulnerabilityFilters()" data-i18n="vulnerabilityPage.clear">清除</button>
<div class="vulnerability-filter-actions">
<div class="vulnerability-more-filters-anchor">
<button type="button" class="btn-secondary vulnerability-more-filters-btn" id="vulnerability-more-filters-btn"
aria-expanded="false" aria-haspopup="dialog" aria-controls="vulnerability-more-filters-popover">
<span data-i18n="vulnerabilityPage.moreFilters">更多筛选</span>
<span class="vulnerability-filter-advanced-badge" id="vulnerability-advanced-badge" hidden></span>
</button>
<div class="vulnerability-more-filters-popover" id="vulnerability-more-filters-popover" hidden role="dialog"
aria-labelledby="vulnerability-more-filters-title">
<p class="vulnerability-more-filters-popover-title" id="vulnerability-more-filters-title" data-i18n="vulnerabilityPage.moreFilters">更多筛选</p>
<div class="vulnerability-more-filters-popover-body">
<label class="vulnerability-filter-field vulnerability-filter-field--full">
<span data-i18n="vulnerabilityPage.vulnId">漏洞 ID</span>
<input type="text" id="vulnerability-exact-id-filter"
data-i18n="vulnerabilityPage.filterExactId" data-i18n-attr="placeholder" placeholder="精确匹配漏洞 ID" />
</label>
<label class="vulnerability-filter-field">
<span data-i18n="vulnerabilityPage.conversationId">会话 ID</span>
<input type="text" id="vulnerability-conversation-filter" list="vulnerability-conversation-suggestions"
data-i18n="vulnerabilityPage.filterConversation" data-i18n-attr="placeholder" placeholder="筛选特定会话" />
</label>
<label class="vulnerability-filter-field">
<span data-i18n="vulnerabilityPage.taskOrQueueId">任务 / 队列 ID</span>
<input type="text" id="vulnerability-task-filter" list="vulnerability-task-suggestions"
data-i18n="vulnerabilityPage.filterTaskOrQueue" data-i18n-attr="placeholder" placeholder="筛选任务或队列 ID" />
</label>
<label class="vulnerability-filter-field">
<span data-i18n="vulnerabilityPage.conversationTag">对话标签</span>
<input type="text" id="vulnerability-conversation-tag-filter" list="vulnerability-conversation-tag-suggestions"
data-i18n="vulnerabilityPage.filterConversationTag" data-i18n-attr="placeholder" placeholder="筛选对话标签" />
</label>
<label class="vulnerability-filter-field">
<span data-i18n="vulnerabilityPage.taskTag">任务标签</span>
<input type="text" id="vulnerability-task-tag-filter" list="vulnerability-task-tag-suggestions"
data-i18n="vulnerabilityPage.filterTaskTag" data-i18n-attr="placeholder" placeholder="筛选任务标签" />
</label>
</div>
<div class="vulnerability-more-filters-popover-footer">
<button type="button" class="btn-secondary" id="vulnerability-more-filters-reset" data-i18n="vulnerabilityPage.clearAdvanced">清空</button>
<button type="button" class="btn-primary" id="vulnerability-more-filters-apply" data-i18n="vulnerabilityPage.applyFilters">应用</button>
</div>
</div>
</div>
<button type="button" class="vulnerability-filter-clear-btn" id="vulnerability-filter-clear-btn"
onclick="clearVulnerabilityFilters()" hidden data-i18n="vulnerabilityPage.clearAll">重置全部</button>
</div>
</div>
<select id="vulnerability-severity-filter" class="vulnerability-severity-sync" hidden aria-hidden="true" tabindex="-1">
<option value=""></option>
@@ -1472,34 +1521,8 @@
<option value="info">info</option>
</select>
</div>
<div class="vulnerability-filter-advanced-wrap">
<button type="button" class="vulnerability-filter-advanced-toggle" id="vulnerability-advanced-toggle"
aria-expanded="false" aria-controls="vulnerability-advanced-filters"
onclick="toggleVulnerabilityAdvancedFilters(event)">
<span class="vulnerability-filter-advanced-chevron" aria-hidden="true"></span>
<span data-i18n="vulnerabilityPage.advancedFilters">高级筛选</span>
<span class="vulnerability-filter-advanced-badge" id="vulnerability-advanced-badge" hidden></span>
</button>
<div class="vulnerability-filter-advanced" id="vulnerability-advanced-filters" hidden>
<label class="vulnerability-filter-field">
<span data-i18n="vulnerabilityPage.conversationId">会话 ID</span>
<input type="text" id="vulnerability-conversation-filter" list="vulnerability-conversation-suggestions" placeholder="回车筛选" />
</label>
<label class="vulnerability-filter-field">
<span data-i18n="vulnerabilityPage.taskOrQueueId">任务 / 队列 ID</span>
<input type="text" id="vulnerability-task-filter" list="vulnerability-task-suggestions" placeholder="回车筛选" />
</label>
<label class="vulnerability-filter-field">
<span data-i18n="vulnerabilityPage.conversationTag">对话标签</span>
<input type="text" id="vulnerability-conversation-tag-filter" list="vulnerability-conversation-tag-suggestions" placeholder="回车筛选" />
</label>
<label class="vulnerability-filter-field">
<span data-i18n="vulnerabilityPage.taskTag">任务标签</span>
<input type="text" id="vulnerability-task-tag-filter" list="vulnerability-task-tag-suggestions" placeholder="回车筛选" />
</label>
</div>
</div>
<div class="vulnerability-filter-chips" id="vulnerability-filter-chips" hidden>
<span class="vulnerability-filter-chips-label" data-i18n="vulnerabilityPage.activeFilters">已选条件</span>
<div class="vulnerability-filter-chips-list" id="vulnerability-filter-chips-list" role="list"></div>
</div>
<datalist id="vulnerability-conversation-suggestions"></datalist>