diff --git a/web/static/js/chat.js b/web/static/js/chat.js index 34e2c43e..99caad01 100644 --- a/web/static/js/chat.js +++ b/web/static/js/chat.js @@ -1244,6 +1244,9 @@ function addMessage(role, content, mcpExecutionIds = null, progressId = null, cr const msgTimeOpts = { hour: '2-digit', minute: '2-digit' }; if (msgTimeLocale === 'zh-CN') msgTimeOpts.hour12 = false; timeDiv.textContent = messageTime.toLocaleTimeString(msgTimeLocale, msgTimeOpts); + try { + timeDiv.dataset.messageTime = messageTime.toISOString(); + } catch (e) { /* ignore */ } contentWrapper.appendChild(timeDiv); // 如果有MCP执行ID或进度ID,添加查看详情区域(统一使用"渗透测试详情"样式) @@ -1594,10 +1597,19 @@ async function showMCPDetail(executionId) { const normalizedStatus = (exec.status || 'unknown').toLowerCase(); statusEl.textContent = getStatusText(exec.status); statusEl.className = `status-chip status-${normalizedStatus}`; + try { + statusEl.dataset.detailStatus = (exec.status || '') + ''; + } catch (e) { /* ignore */ } const detailTimeLocale = (typeof window.__locale === 'string' && window.__locale.startsWith('zh')) ? 'zh-CN' : 'en-US'; - document.getElementById('detail-time').textContent = exec.startTime - ? new Date(exec.startTime).toLocaleString(detailTimeLocale) - : '—'; + const detailTimeEl = document.getElementById('detail-time'); + if (detailTimeEl) { + detailTimeEl.textContent = exec.startTime + ? new Date(exec.startTime).toLocaleString(detailTimeLocale) + : '—'; + try { + detailTimeEl.dataset.detailTimeIso = exec.startTime ? new Date(exec.startTime).toISOString() : ''; + } catch (e) { /* ignore */ } + } // 请求参数 const requestData = { @@ -5273,9 +5285,60 @@ function closeBatchManageModal() { allConversationsForBatch = []; } -// 语言切换时刷新批量管理模态框标题(若当前正在显示);并刷新对话列表时间格式与系统就绪提示 +// 语言切换时刷新当前聊天页内的时间与动态文案(消息时间、执行流程时间由 monitor 的 refreshProgressAndTimelineI18n 处理) +function refreshChatPanelI18n() { + const locale = (typeof window.__locale === 'string' && window.__locale.startsWith('zh')) ? 'zh-CN' : 'en-US'; + const timeOpts = { hour: '2-digit', minute: '2-digit' }; + if (locale === 'zh-CN') timeOpts.hour12 = false; + const t = typeof window.t === 'function' ? window.t : function (k) { return k; }; + + const messagesEl = document.getElementById('chat-messages'); + if (messagesEl) { + messagesEl.querySelectorAll('.message-time[data-message-time]').forEach(function (el) { + try { + const d = new Date(el.dataset.messageTime); + if (!isNaN(d.getTime())) { + el.textContent = d.toLocaleTimeString(locale, timeOpts); + } + } catch (e) { /* ignore */ } + }); + messagesEl.querySelectorAll('.mcp-call-label').forEach(function (el) { + el.textContent = '\uD83D\uDCCB ' + t('chat.penetrationTestDetail'); + }); + messagesEl.querySelectorAll('.process-detail-btn').forEach(function (btn) { + const span = btn.querySelector('span'); + if (!span) return; + const assistantEl = btn.closest('.message.assistant'); + const messageId = assistantEl && assistantEl.id; + const detailsId = messageId ? 'process-details-' + messageId : ''; + const timeline = detailsId ? document.getElementById(detailsId) && document.getElementById(detailsId).querySelector('.progress-timeline') : null; + const expanded = timeline && timeline.classList.contains('expanded'); + span.textContent = expanded ? t('tasks.collapseDetail') : t('chat.expandDetail'); + }); + } + + const mcpModal = document.getElementById('mcp-detail-modal'); + if (mcpModal && mcpModal.style.display === 'block') { + const detailTimeEl = document.getElementById('detail-time'); + if (detailTimeEl && detailTimeEl.dataset.detailTimeIso) { + try { + const d = new Date(detailTimeEl.dataset.detailTimeIso); + if (!isNaN(d.getTime())) { + detailTimeEl.textContent = d.toLocaleString(locale); + } + } catch (e) { /* ignore */ } + } + const statusEl = document.getElementById('detail-status'); + if (statusEl && statusEl.dataset.detailStatus !== undefined && typeof getStatusText === 'function') { + statusEl.textContent = getStatusText(statusEl.dataset.detailStatus); + } + } +} + +// 语言切换时刷新批量管理模态框标题(若当前正在显示);并刷新对话列表时间格式与系统就绪提示;刷新当前页消息时间与动态文案 document.addEventListener('languagechange', function () { refreshSystemReadyMessageBubbles(); + refreshChatPanelI18n(); const modal = document.getElementById('batch-manage-modal'); if (modal && modal.style.display === 'flex') { updateBatchManageTitle(allConversationsForBatch.length); diff --git a/web/static/js/monitor.js b/web/static/js/monitor.js index b484abae..7843ec0d 100644 --- a/web/static/js/monitor.js +++ b/web/static/js/monitor.js @@ -896,17 +896,28 @@ function addTimelineItem(timeline, type, options) { item.className = `timeline-item timeline-item-${type}`; // 记录类型与参数,便于 languagechange 时刷新标题文案 item.dataset.timelineType = type; - if (type === 'iteration' && options.iterationN != null) { - item.dataset.iterationN = String(options.iterationN); + if (type === 'iteration') { + const n = options.iterationN != null ? options.iterationN : (options.data && options.data.iteration != null ? options.data.iteration : 1); + item.dataset.iterationN = String(n); + } + if (type === 'progress' && options.message) { + item.dataset.progressMessage = options.message; } if (type === 'tool_calls_detected' && options.data && options.data.count != null) { item.dataset.toolCallsCount = String(options.data.count); } - // 保存事件时间 ISO,语言切换时可重算时间格式 - try { - item.dataset.createdAtIso = eventTime.toISOString(); - } catch (e) { /* ignore */ } - + if (type === 'tool_call' && options.data) { + const d = options.data; + item.dataset.toolName = (d.toolName != null && d.toolName !== '') ? String(d.toolName) : ''; + item.dataset.toolIndex = (d.index != null) ? String(d.index) : '0'; + item.dataset.toolTotal = (d.total != null) ? String(d.total) : '0'; + } + if (type === 'tool_result' && options.data) { + const d = options.data; + item.dataset.toolName = (d.toolName != null && d.toolName !== '') ? String(d.toolName) : ''; + item.dataset.toolSuccess = d.success !== false ? '1' : '0'; + } + // 使用传入的createdAt时间,如果没有则使用当前时间(向后兼容) let eventTime; if (options.createdAt) { @@ -925,7 +936,11 @@ function addTimelineItem(timeline, type, options) { } else { eventTime = new Date(); } - + // 保存事件时间 ISO,语言切换时可重算时间格式 + try { + item.dataset.createdAtIso = eventTime.toISOString(); + } catch (e) { /* ignore */ } + const timeLocale = getCurrentTimeLocale(); const timeOpts = getTimeFormatOptions(); const time = eventTime.toLocaleTimeString(timeLocale, timeOpts); @@ -948,7 +963,7 @@ function addTimelineItem(timeline, type, options) {
- ${escapeHtml(paramsLabel)} + ${escapeHtml(paramsLabel)}
${escapeHtml(JSON.stringify(args, null, 2))}
@@ -965,9 +980,9 @@ function addTimelineItem(timeline, type, options) { content += `
- ${escapeHtml(execResultLabel)} + ${escapeHtml(execResultLabel)}
${escapeHtml(resultStr)}
- ${data.executionId ? `
${escapeHtml(execIdLabel)} ${escapeHtml(data.executionId)}
` : ''} + ${data.executionId ? `
${escapeHtml(execIdLabel)} ${escapeHtml(data.executionId)}
` : ''}
`; @@ -1771,6 +1786,11 @@ function refreshProgressAndTimelineI18n() { titleEl.textContent = '\uD83D\uDD0D ' + translateProgressMessage(raw); } }); + // 转换后的详情区顶栏「渗透测试详情」:仅刷新不在 .progress-message 内的 progress 标题 + document.querySelectorAll('.progress-container .progress-header .progress-title').forEach(function (titleEl) { + if (titleEl.closest('.progress-message')) return; + titleEl.textContent = '\uD83D\uDCCB ' + _t('chat.penetrationTestDetail'); + }); // 时间线项:按类型重算标题,并重绘时间戳 document.querySelectorAll('.timeline-item').forEach(function (item) { @@ -1786,6 +1806,20 @@ function refreshProgressAndTimelineI18n() { } else if (type === 'tool_calls_detected' && item.dataset.toolCallsCount != null) { const count = parseInt(item.dataset.toolCallsCount, 10) || 0; titleSpan.textContent = '\uD83D\uDD27 ' + _t('chat.toolCallsDetected', { count: count }); + } else if (type === 'tool_call' && (item.dataset.toolName !== undefined || item.dataset.toolIndex !== undefined)) { + const name = (item.dataset.toolName != null && item.dataset.toolName !== '') ? item.dataset.toolName : _t('chat.unknownTool'); + const index = parseInt(item.dataset.toolIndex, 10) || 0; + const total = parseInt(item.dataset.toolTotal, 10) || 0; + titleSpan.textContent = '\uD83D\uDD27 ' + _t('chat.callTool', { name: name, index: index, total: total }); + } else if (type === 'tool_result' && (item.dataset.toolName !== undefined || item.dataset.toolSuccess !== undefined)) { + const name = (item.dataset.toolName != null && item.dataset.toolName !== '') ? item.dataset.toolName : _t('chat.unknownTool'); + const success = item.dataset.toolSuccess === '1'; + const icon = success ? '\u2705 ' : '\u274C '; + titleSpan.textContent = icon + (success ? _t('chat.toolExecComplete', { name: name }) : _t('chat.toolExecFailed', { name: name })); + } else if (type === 'cancelled') { + titleSpan.textContent = '\u26D4 ' + _t('chat.taskCancelled'); + } else if (type === 'progress' && item.dataset.progressMessage !== undefined) { + titleSpan.textContent = typeof window.translateProgressMessage === 'function' ? window.translateProgressMessage(item.dataset.progressMessage) : item.dataset.progressMessage; } if (timeSpan && item.dataset.createdAtIso) { const d = new Date(item.dataset.createdAtIso);