Add files via upload

This commit is contained in:
公明
2025-12-26 01:33:07 +08:00
committed by GitHub
parent 7585b9d603
commit cd48cfa67b
8 changed files with 149 additions and 21 deletions

View File

@@ -5427,8 +5427,10 @@ header {
font-size: 0.875rem;
color: var(--text-primary);
overflow: hidden;
text-overflow: ellipsis;
text-overflow: clip;
white-space: nowrap;
word-break: keep-all;
/* 完全依赖JavaScript截断禁用CSS的ellipsis以避免在UTF-8多字节字符中间截断 */
}
.batch-table-col-time {

View File

@@ -1356,7 +1356,9 @@ function createConversationListItem(conversation) {
const title = document.createElement('div');
title.className = 'conversation-title';
title.textContent = conversation.title || '未命名对话';
const titleText = conversation.title || '未命名对话';
title.textContent = safeTruncateText(titleText, 60);
title.title = titleText; // 设置完整标题以便悬停查看
contentWrapper.appendChild(title);
const time = document.createElement('div');
@@ -3916,7 +3918,9 @@ function createConversationListItemWithMenu(conversation, isPinned) {
const title = document.createElement('div');
title.className = 'conversation-title';
title.textContent = conversation.title || '未命名对话';
const titleText = conversation.title || '未命名对话';
title.textContent = safeTruncateText(titleText, 60);
title.title = titleText; // 设置完整标题以便悬停查看
titleWrapper.appendChild(title);
if (isPinned) {
@@ -4394,8 +4398,13 @@ async function showMoveToGroupSubmenu() {
groupsCache = [];
}
// 如果有分组,显示所有分组(排除当前分组)
// 如果有分组,显示所有分组(排除对话已所在的分组)
if (groupsCache.length > 0) {
// 检查对话当前所在的分组ID
const conversationCurrentGroupId = contextMenuConversationId
? conversationGroupMappingCache[contextMenuConversationId]
: null;
groupsCache.forEach(group => {
// 验证分组对象是否有效
if (!group || !group.id || !group.name) {
@@ -4403,8 +4412,8 @@ async function showMoveToGroupSubmenu() {
return;
}
// 如果当前分组详情页面,不显示当前分组
if (currentGroupId && group.id === currentGroupId) {
// 如果对话已经在当前分组中,不显示该分组(因为已经在里面了)
if (conversationCurrentGroupId && group.id === conversationCurrentGroupId) {
return;
}
@@ -4570,16 +4579,23 @@ async function moveConversationToGroup(convId, groupId) {
currentConversationGroupId = groupId;
}
// 重新加载分组映射缓存,确保数据同步
await loadConversationGroupMapping();
// 如果当前在分组详情页面,重新加载分组对话
if (currentGroupId) {
// 如果从当前分组移出,或者移动到当前分组,都需要重新加载
if (currentGroupId === oldGroupId || currentGroupId === groupId) {
loadGroupConversations(currentGroupId);
await loadGroupConversations(currentGroupId);
}
} else {
// 如果不在分组详情页面,刷新最近对话列表
loadConversationsWithGroups();
}
// 如果旧分组和新分组不同,且用户正在查看旧分组,也需要刷新旧分组
// 但上面的逻辑已经处理了这种情况currentGroupId === oldGroupId
// 刷新分组列表,更新高亮状态
await loadGroups();
} catch (error) {
@@ -4718,6 +4734,45 @@ async function showBatchManageModal() {
}
}
// 安全截断中文字符串,避免在汉字中间截断
function safeTruncateText(text, maxLength = 50) {
if (!text || typeof text !== 'string') {
return text || '';
}
// 使用 Array.from 将字符串转换为字符数组(正确处理 Unicode 代理对)
const chars = Array.from(text);
// 如果文本长度未超过限制,直接返回
if (chars.length <= maxLength) {
return text;
}
// 截断到最大长度(基于字符数,而不是代码单元)
let truncatedChars = chars.slice(0, maxLength);
// 尝试在标点符号或空格处截断,使截断更自然
// 在截断点往前查找合适的断点不超过20%的长度)
const searchRange = Math.floor(maxLength * 0.2);
const breakChars = ['', '。', '、', ' ', ',', '.', ';', ':', '!', '?', '', '', '/', '\\', '-', '_'];
let bestBreakPos = truncatedChars.length;
for (let i = truncatedChars.length - 1; i >= truncatedChars.length - searchRange && i >= 0; i--) {
if (breakChars.includes(truncatedChars[i])) {
bestBreakPos = i + 1; // 在标点符号后断开
break;
}
}
// 如果找到合适的断点,使用它;否则使用原截断位置
if (bestBreakPos < truncatedChars.length) {
truncatedChars = truncatedChars.slice(0, bestBreakPos);
}
// 将字符数组转换回字符串,并添加省略号
return truncatedChars.join('') + '...';
}
// 渲染批量管理对话列表
function renderBatchConversations(filtered = null) {
const list = document.getElementById('batch-conversations-list');
@@ -4738,7 +4793,12 @@ function renderBatchConversations(filtered = null) {
const name = document.createElement('div');
name.className = 'batch-table-col-name';
name.textContent = conv.title || '未命名对话';
const originalTitle = conv.title || '未命名对话';
// 使用安全截断函数限制最大长度为45个字符留出空间显示省略号
const truncatedTitle = safeTruncateText(originalTitle, 45);
name.textContent = truncatedTitle;
// 设置title属性以显示完整文本鼠标悬停时
name.title = originalTitle;
const time = document.createElement('div');
time.className = 'batch-table-col-time';
@@ -5114,7 +5174,9 @@ async function loadGroupConversations(groupId) {
const title = document.createElement('div');
title.className = 'group-conversation-title';
title.textContent = fullConv.title || conv.title || '未命名对话';
const titleText = fullConv.title || conv.title || '未命名对话';
title.textContent = safeTruncateText(titleText, 60);
title.title = titleText; // 设置完整标题以便悬停查看
titleWrapper.appendChild(title);
// 如果对话在分组中置顶,显示置顶图标

View File

@@ -2,6 +2,7 @@
let currentVulnerabilityId = null;
let vulnerabilityFilters = {
id: '',
conversation_id: '',
severity: '',
status: ''
@@ -80,6 +81,9 @@ async function loadVulnerabilities() {
params.append('limit', '100');
params.append('offset', '0');
if (vulnerabilityFilters.id) {
params.append('id', vulnerabilityFilters.id);
}
if (vulnerabilityFilters.conversation_id) {
params.append('conversation_id', vulnerabilityFilters.conversation_id);
}
@@ -172,6 +176,7 @@ function renderVulnerabilities(vulnerabilities) {
<div class="vulnerability-content" id="content-${vuln.id}" style="display: none;">
${vuln.description ? `<div class="vulnerability-description">${escapeHtml(vuln.description)}</div>` : ''}
<div class="vulnerability-details">
<div class="detail-item"><strong>漏洞ID:</strong> <code>${escapeHtml(vuln.id)}</code></div>
${vuln.type ? `<div class="detail-item"><strong>类型:</strong> ${escapeHtml(vuln.type)}</div>` : ''}
${vuln.target ? `<div class="detail-item"><strong>目标:</strong> ${escapeHtml(vuln.target)}</div>` : ''}
<div class="detail-item"><strong>会话ID:</strong> <code>${escapeHtml(vuln.conversation_id)}</code></div>
@@ -317,6 +322,7 @@ function closeVulnerabilityModal() {
// 筛选漏洞
function filterVulnerabilities() {
vulnerabilityFilters.id = document.getElementById('vulnerability-id-filter').value.trim();
vulnerabilityFilters.conversation_id = document.getElementById('vulnerability-conversation-filter').value.trim();
vulnerabilityFilters.severity = document.getElementById('vulnerability-severity-filter').value;
vulnerabilityFilters.status = document.getElementById('vulnerability-status-filter').value;
@@ -327,11 +333,13 @@ function filterVulnerabilities() {
// 清除筛选
function clearVulnerabilityFilters() {
document.getElementById('vulnerability-id-filter').value = '';
document.getElementById('vulnerability-conversation-filter').value = '';
document.getElementById('vulnerability-severity-filter').value = '';
document.getElementById('vulnerability-status-filter').value = '';
vulnerabilityFilters = {
id: '',
conversation_id: '',
severity: '',
status: ''

View File

@@ -482,6 +482,10 @@
<!-- 筛选和搜索 -->
<div class="vulnerability-controls">
<div class="vulnerability-filters">
<label>
漏洞ID
<input type="text" id="vulnerability-id-filter" placeholder="搜索漏洞ID" />
</label>
<label>
会话ID
<input type="text" id="vulnerability-conversation-filter" placeholder="筛选特定会话" />