diff --git a/web/static/css/style.css b/web/static/css/style.css index 0cb32f9e..b5cd4890 100644 --- a/web/static/css/style.css +++ b/web/static/css/style.css @@ -2326,6 +2326,8 @@ html[data-theme="dark"] .openapi-doc-btn:hover { .conversation-search-box { position: relative; + display: flex; + align-items: center; margin-bottom: 10px; } @@ -2378,8 +2380,6 @@ html[data-theme="dark"] .openapi-doc-btn:hover { .conversation-search-clear { position: absolute; right: 8px; - top: 50%; - transform: translateY(-50%); width: 20px; height: 20px; padding: 0; @@ -2391,7 +2391,7 @@ html[data-theme="dark"] .openapi-doc-btn:hover { justify-content: center; color: var(--text-muted); transition: color 0.2s ease; - border-radius: 4px; + border-radius: 50%; } .conversation-search-clear:hover { @@ -2400,8 +2400,9 @@ html[data-theme="dark"] .openapi-doc-btn:hover { } .conversation-search-clear svg { - width: 14px; - height: 14px; + width: 16px; + height: 16px; + display: block; } .conversation-project-filter { @@ -22944,9 +22945,17 @@ html[data-theme="dark"] .vulnerability-details code.vuln-detail-field-value { border-color: var(--border-color); } -.vuln-detail-field__copy { +.vuln-detail-field__actions { + display: flex; flex-shrink: 0; + align-items: flex-start; + gap: 2px; margin-top: 2px; +} + +.vuln-detail-field__copy, +.vuln-detail-field__action { + flex-shrink: 0; padding: 6px; line-height: 0; border-radius: 6px; @@ -22956,7 +22965,8 @@ html[data-theme="dark"] .vulnerability-details code.vuln-detail-field-value { cursor: pointer; } -.vuln-detail-field__copy:hover { +.vuln-detail-field__copy:hover, +.vuln-detail-field__action:hover { color: var(--accent-color); background: var(--bg-primary); border-color: var(--border-color); diff --git a/web/static/i18n/en-US.json b/web/static/i18n/en-US.json index a2ba619b..24188e6f 100644 --- a/web/static/i18n/en-US.json +++ b/web/static/i18n/en-US.json @@ -2140,6 +2140,7 @@ "projectBindFailed": "Failed to update project binding", "projectBindOk": "Project binding updated", "detailConversationId": "Conversation ID", + "openConversation": "View conversation", "detailTaskId": "Task ID", "detailTaskQueueId": "Task queue ID", "detailConversationTag": "Conversation tag", diff --git a/web/static/i18n/zh-CN.json b/web/static/i18n/zh-CN.json index 36a3b9f6..f25c01a3 100644 --- a/web/static/i18n/zh-CN.json +++ b/web/static/i18n/zh-CN.json @@ -2128,6 +2128,7 @@ "projectBindFailed": "绑定项目失败", "projectBindOk": "已更新项目绑定", "detailConversationId": "会话ID", + "openConversation": "查看对话", "detailTaskId": "任务ID", "detailTaskQueueId": "任务队列ID", "detailConversationTag": "对话标签", diff --git a/web/static/js/router.js b/web/static/js/router.js index 173ad844..76d9a154 100644 --- a/web/static/js/router.js +++ b/web/static/js/router.js @@ -50,6 +50,29 @@ function scheduleChatConversationFromHash(delayMs) { }, delayMs); } +/** 跳转到指定对话:单次切页 + 单次加载,避免 hashchange 与手动 load 重复触发导致闪烁 */ +function navigateToConversation(conversationId) { + const cid = String(conversationId || '').trim(); + if (!cid) return; + const targetHash = 'chat?conversation=' + encodeURIComponent(cid); + const alreadyOnChat = currentPage === 'chat'; + + if (window.location.hash.slice(1) !== targetHash) { + history.replaceState(null, '', '#' + targetHash); + } + + if (!alreadyOnChat) { + switchPage('chat'); + } + + if (typeof loadConversation === 'function') { + void loadConversation(cid); + } else if (typeof window.loadConversation === 'function') { + void window.loadConversation(cid); + } +} +window.navigateToConversation = navigateToConversation; + // 初始化路由 function initRouter() { // 从URL hash读取页面(如果有) diff --git a/web/static/js/vulnerability.js b/web/static/js/vulnerability.js index a416e77c..65369d7d 100644 --- a/web/static/js/vulnerability.js +++ b/web/static/js/vulnerability.js @@ -1436,7 +1436,7 @@ function renderVulnerabilities(vulnerabilities, renderOptions) { ${vulnDetailProjectField(vuln)} ${vuln.type ? vulnDetailField(vulnT('vulnerabilityPage.detailType'), vuln.type, false) : ''} ${vuln.target ? vulnDetailField(vulnT('vulnerabilityPage.detailTarget'), vuln.target, false) : ''} - ${vulnDetailField(vulnT('vulnerabilityPage.detailConversationId'), vuln.conversation_id, true)} + ${vulnDetailConversationField(vuln.conversation_id)} ${vuln.task_id ? vulnDetailField(vulnT('vulnerabilityPage.detailTaskId'), vuln.task_id, true) : ''} ${vuln.task_queue_id ? vulnDetailField(vulnT('vulnerabilityPage.detailTaskQueueId'), vuln.task_queue_id, true) : ''} ${vuln.conversation_tag ? vulnDetailField(vulnT('vulnerabilityPage.detailConversationTag'), vuln.conversation_tag, false) : ''} @@ -2025,10 +2025,39 @@ function vulnDetailField(label, value, asCode) { `; return `
${escapeHtml(label)}
-
${valueEl}${copyBtn}
+
${valueEl}
${copyBtn}
`; } +function vulnDetailConversationField(conversationId) { + if (conversationId === undefined || conversationId === null || String(conversationId).trim() === '') { + return ''; + } + const s = String(conversationId).trim(); + const enc = encodeURIComponent(s); + const copyTitle = escapeHtml(vulnT('common.copy')); + const openTitle = escapeHtml(vulnT('vulnerabilityPage.openConversation')); + const qId = JSON.stringify(s).replace(/"/g, '"'); + const openBtn = ``; + const copyBtn = ``; + return `
+
${escapeHtml(vulnT('vulnerabilityPage.detailConversationId'))}
+
${escapeHtml(s)}
${openBtn}${copyBtn}
+
`; +} + +function openVulnerabilityConversation(conversationId) { + if (typeof navigateToConversation === 'function') { + navigateToConversation(conversationId); + } else if (typeof window.navigateToConversation === 'function') { + window.navigateToConversation(conversationId); + } +} + function vulnNarrativeSection(label, value, options) { if (value === undefined || value === null || String(value).trim() === '') return ''; const opts = options || {}; @@ -2382,3 +2411,4 @@ window.setVulnerabilityIdFilter = setVulnerabilityIdFilter; window.bindVulnerabilityProject = bindVulnerabilityProject; window.buildVulnerabilityProjectOptionsHtml = buildVulnerabilityProjectOptionsHtml; window.changeVulnerabilityStatus = changeVulnerabilityStatus; +window.openVulnerabilityConversation = openVulnerabilityConversation; diff --git a/web/templates/index.html b/web/templates/index.html index 089bb097..a9683bfc 100644 --- a/web/templates/index.html +++ b/web/templates/index.html @@ -825,9 +825,9 @@ onkeypress="if(event.key === 'Enter') handleConversationSearch(this.value)" /> @@ -5308,7 +5308,7 @@ - +