mirror of
https://github.com/Ed1s0nZ/CyberStrikeAI.git
synced 2026-06-14 18:17:54 +02:00
Add files via upload
This commit is contained in:
@@ -1371,7 +1371,6 @@
|
||||
Modal
|
||||
============================================================================ */
|
||||
|
||||
/* Toast 须高于模态遮罩 (10050),避免被 backdrop-filter 模糊 */
|
||||
#c2-toast-container {
|
||||
z-index: 10100 !important;
|
||||
}
|
||||
@@ -1379,9 +1378,7 @@
|
||||
.c2-modal-overlay {
|
||||
position: fixed;
|
||||
top: 0; left: 0; right: 0; bottom: 0;
|
||||
background: rgba(15, 23, 42, 0.5);
|
||||
backdrop-filter: blur(8px);
|
||||
-webkit-backdrop-filter: blur(8px);
|
||||
background: rgba(15, 23, 42, 0.52);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
@@ -1404,7 +1401,8 @@
|
||||
overflow-y: auto;
|
||||
box-shadow: var(--c2-shadow-lg);
|
||||
border: 1px solid var(--c2-border);
|
||||
animation: c2-slide-up 0.2s ease-out;
|
||||
animation: c2-slide-up 0.18s ease-out;
|
||||
contain: layout style paint;
|
||||
}
|
||||
|
||||
@keyframes c2-slide-up {
|
||||
|
||||
@@ -3326,9 +3326,9 @@ header {
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(0, 0, 0, 0.6);
|
||||
background-color: rgba(15, 23, 42, 0.52);
|
||||
overflow: auto;
|
||||
animation: fadeIn 0.2s ease-in;
|
||||
animation: fadeIn 0.15s ease-out;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
@@ -3343,8 +3343,9 @@ header {
|
||||
flex-direction: column;
|
||||
box-shadow: var(--shadow-lg);
|
||||
border: 1px solid var(--border-color);
|
||||
animation: slideDown 0.3s ease-out;
|
||||
animation: slideDown 0.18s ease-out;
|
||||
overflow: hidden;
|
||||
contain: layout style paint;
|
||||
}
|
||||
|
||||
@keyframes slideDown {
|
||||
@@ -19374,6 +19375,8 @@ tr.mcp-stats-tool-row[data-tool-name]:focus-visible {
|
||||
cursor: pointer;
|
||||
transition: all 0.2s cubic-bezier(0.16, 1, 0.3, 1);
|
||||
position: relative;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.role-selection-item-main:hover {
|
||||
@@ -19436,6 +19439,10 @@ tr.mcp-stats-tool-row[data-tool-name]:focus-visible {
|
||||
margin: 0;
|
||||
transition: color 0.2s cubic-bezier(0.16, 1, 0.3, 1);
|
||||
letter-spacing: -0.01em;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
.role-selection-item-main.selected .role-selection-item-name-main {
|
||||
@@ -19443,6 +19450,10 @@ tr.mcp-stats-tool-row[data-tool-name]:focus-visible {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.role-selection-item-main.selected .role-selection-item-content-main {
|
||||
padding-right: 24px;
|
||||
}
|
||||
|
||||
.role-selection-item-description-main {
|
||||
font-size: 0.75rem;
|
||||
color: #666666;
|
||||
@@ -23777,10 +23788,8 @@ button.chat-files-dropdown-item:hover:not(:disabled) {
|
||||
justify-content: center;
|
||||
padding: 24px 16px;
|
||||
box-sizing: border-box;
|
||||
background: rgba(15, 23, 42, 0.45);
|
||||
backdrop-filter: blur(4px);
|
||||
-webkit-backdrop-filter: blur(4px);
|
||||
animation: projectsOverlayIn 0.2s ease-out;
|
||||
background: rgba(15, 23, 42, 0.52);
|
||||
animation: projectsOverlayIn 0.15s ease-out;
|
||||
}
|
||||
@keyframes projectsOverlayIn {
|
||||
from { opacity: 0; }
|
||||
@@ -23798,7 +23807,8 @@ button.chat-files-dropdown-item:hover:not(:disabled) {
|
||||
0 24px 48px rgba(15, 23, 42, 0.18),
|
||||
0 0 0 1px rgba(15, 23, 42, 0.06);
|
||||
overflow: hidden;
|
||||
animation: projectsDialogIn 0.25s cubic-bezier(0.22, 1, 0.36, 1);
|
||||
animation: projectsDialogIn 0.18s cubic-bezier(0.22, 1, 0.36, 1);
|
||||
contain: layout style paint;
|
||||
}
|
||||
.projects-modal-dialog--wide {
|
||||
max-width: 640px;
|
||||
@@ -23859,6 +23869,13 @@ button.chat-files-dropdown-item:hover:not(:disabled) {
|
||||
padding: 10px 12px;
|
||||
font-size: 0.875rem;
|
||||
transition: border-color 0.15s, box-shadow 0.15s;
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
#project-modal-name {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.projects-modal-body .form-input:focus {
|
||||
outline: none;
|
||||
@@ -23882,7 +23899,8 @@ button.chat-files-dropdown-item:hover:not(:disabled) {
|
||||
.projects-modal-footer .btn-primary {
|
||||
min-width: 100px;
|
||||
}
|
||||
body.projects-modal-open {
|
||||
body.projects-modal-open,
|
||||
body.app-modal-open {
|
||||
overflow: hidden;
|
||||
}
|
||||
.fact-detail-prev-wrap {
|
||||
@@ -24030,8 +24048,11 @@ body.projects-modal-open {
|
||||
/* 对话区项目选择器(与角色/代理模式共用 role-selector-*) */
|
||||
.project-selector-wrapper .role-selector-text {
|
||||
max-width: 108px;
|
||||
min-width: 0;
|
||||
flex-shrink: 1;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.chat-project-panel {
|
||||
width: 280px;
|
||||
@@ -24051,6 +24072,7 @@ body.projects-modal-open {
|
||||
padding-right: 0;
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
.chat-project-panel .role-selection-item-main {
|
||||
width: 100%;
|
||||
|
||||
+20
-17
@@ -105,45 +105,48 @@ function showAddMarkdownAgentModal() {
|
||||
document.getElementById('agent-md-bind-role').value = '';
|
||||
document.getElementById('agent-md-max-iter').value = '0';
|
||||
document.getElementById('agent-md-instruction').value = '';
|
||||
if (modal) modal.style.display = 'flex';
|
||||
openAppModal('agent-md-modal');
|
||||
}
|
||||
|
||||
async function editMarkdownAgent(filename) {
|
||||
if (!filename) return;
|
||||
const modal = document.getElementById('agent-md-modal');
|
||||
const title = document.getElementById('agent-md-modal-title');
|
||||
const row = document.getElementById('agent-md-filename-row');
|
||||
markdownAgentsEditingFilename = null;
|
||||
markdownAgentsEditingIsOrchestrator = false;
|
||||
if (title) title.textContent = _agentsT('agentsPage.editTitle');
|
||||
if (row) row.style.display = 'none';
|
||||
document.getElementById('agent-md-instruction').value = '';
|
||||
openAppModal('agent-md-modal', { focus: false });
|
||||
try {
|
||||
const r = await apiFetch('/api/multi-agent/markdown-agents/' + encodeURIComponent(filename));
|
||||
const data = await r.json();
|
||||
if (!r.ok) throw new Error(data.error || r.statusText);
|
||||
markdownAgentsEditingFilename = data.filename || filename;
|
||||
markdownAgentsEditingIsOrchestrator = !!data.is_orchestrator;
|
||||
document.getElementById('agent-md-filename-current').value = data.filename || filename;
|
||||
document.getElementById('agent-md-filename').value = data.filename || filename;
|
||||
document.getElementById('agent-md-filename').disabled = true;
|
||||
var roleEl2 = document.getElementById('agent-md-role');
|
||||
if (roleEl2) roleEl2.value = data.is_orchestrator ? 'orchestrator' : 'sub';
|
||||
document.getElementById('agent-md-id').value = data.id || '';
|
||||
document.getElementById('agent-md-name').value = data.name || '';
|
||||
document.getElementById('agent-md-description').value = data.description || '';
|
||||
document.getElementById('agent-md-tools').value = Array.isArray(data.tools) ? data.tools.join(', ') : '';
|
||||
document.getElementById('agent-md-bind-role').value = data.bind_role || '';
|
||||
document.getElementById('agent-md-max-iter').value = String(data.max_iterations != null ? data.max_iterations : 0);
|
||||
document.getElementById('agent-md-instruction').value = data.instruction || '';
|
||||
if (modal) modal.style.display = 'flex';
|
||||
deferModalContent(function () {
|
||||
document.getElementById('agent-md-filename-current').value = data.filename || filename;
|
||||
document.getElementById('agent-md-filename').value = data.filename || filename;
|
||||
document.getElementById('agent-md-filename').disabled = true;
|
||||
var roleEl2 = document.getElementById('agent-md-role');
|
||||
if (roleEl2) roleEl2.value = data.is_orchestrator ? 'orchestrator' : 'sub';
|
||||
document.getElementById('agent-md-id').value = data.id || '';
|
||||
document.getElementById('agent-md-name').value = data.name || '';
|
||||
document.getElementById('agent-md-description').value = data.description || '';
|
||||
document.getElementById('agent-md-tools').value = Array.isArray(data.tools) ? data.tools.join(', ') : '';
|
||||
document.getElementById('agent-md-bind-role').value = data.bind_role || '';
|
||||
document.getElementById('agent-md-max-iter').value = String(data.max_iterations != null ? data.max_iterations : 0);
|
||||
document.getElementById('agent-md-instruction').value = data.instruction || '';
|
||||
document.getElementById('agent-md-name')?.focus();
|
||||
});
|
||||
} catch (e) {
|
||||
closeMarkdownAgentModal();
|
||||
showNotification(_agentsT('agentsPage.loadOneFailed') + ': ' + e.message, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
function closeMarkdownAgentModal() {
|
||||
const modal = document.getElementById('agent-md-modal');
|
||||
if (modal) modal.style.display = 'none';
|
||||
closeAppModal('agent-md-modal');
|
||||
markdownAgentsEditingFilename = null;
|
||||
markdownAgentsEditingIsOrchestrator = false;
|
||||
}
|
||||
|
||||
+38
-33
@@ -533,56 +533,61 @@ async function exportAuditLogsCsv() {
|
||||
}
|
||||
|
||||
function closeAuditDetailModal() {
|
||||
closeAppModal('audit-detail-modal');
|
||||
const el = document.getElementById('audit-detail-modal');
|
||||
if (el) el.remove();
|
||||
syncAppModalBodyLock();
|
||||
}
|
||||
|
||||
async function showAuditLogDetail(id) {
|
||||
if (!id || typeof apiFetch !== 'function') return;
|
||||
const esc = typeof escapeHtml === 'function' ? escapeHtml : function (s) { return String(s || ''); };
|
||||
try {
|
||||
closeAuditDetailModal();
|
||||
const overlay = document.createElement('div');
|
||||
overlay.id = 'audit-detail-modal';
|
||||
overlay.className = 'modal';
|
||||
document.body.appendChild(overlay);
|
||||
openAppModal(overlay, { focus: false });
|
||||
const r = await apiFetch('/api/audit/logs/' + encodeURIComponent(id));
|
||||
if (!r.ok) throw new Error('not found');
|
||||
const data = await r.json();
|
||||
const log = data.log || {};
|
||||
const detail = log.detail ? JSON.stringify(log.detail, null, 2) : '';
|
||||
closeAuditDetailModal();
|
||||
const overlay = document.createElement('div');
|
||||
overlay.id = 'audit-detail-modal';
|
||||
overlay.className = 'modal';
|
||||
overlay.style.display = 'block';
|
||||
const catAction = esc(auditCategoryLabel(log.category || '')) + ' / ' + esc(auditActionLabel(log.action || ''));
|
||||
overlay.innerHTML =
|
||||
'<div class="modal-content" style="max-width: 720px;">' +
|
||||
'<div class="modal-header">' +
|
||||
'<h2>' + esc(auditT('settingsAudit.detailTitle', null, '审计详情')) + '</h2>' +
|
||||
'<span class="modal-close" onclick="closeAuditDetailModal()">×</span>' +
|
||||
'</div>' +
|
||||
'<div class="modal-body audit-detail-body">' +
|
||||
'<p><strong>' + esc(auditT('settingsAudit.detailTime', null, '时间')) + ':</strong> ' + esc(formatAuditTime(log.createdAt)) + '</p>' +
|
||||
'<p><strong>' + esc(auditT('settingsAudit.detailCategory', null, '类别')) + ':</strong> ' + catAction + '</p>' +
|
||||
'<p><strong>' + esc(auditT('settingsAudit.detailResult', null, '结果')) + ':</strong> ' + esc(log.result || '') + '</p>' +
|
||||
'<p><strong>' + esc(auditT('settingsAudit.detailMessage', null, '说明')) + ':</strong> ' + esc(log.message || '') + '</p>' +
|
||||
(log.clientIp ? '<p><strong>IP:</strong> ' + esc(log.clientIp) + '</p>' : '') +
|
||||
(log.sessionHint ? '<p><strong>' + esc(auditT('settingsAudit.detailSession', null, '会话')) + ':</strong> ' + esc(log.sessionHint) + '</p>' : '') +
|
||||
(log.userAgent ? '<p><strong>UA:</strong> ' + esc(log.userAgent) + '</p>' : '') +
|
||||
auditResourceMeta(log) +
|
||||
(detail ? '<pre class="audit-detail-pre">' + esc(detail) + '</pre>' : '') +
|
||||
'</div>' +
|
||||
'<div class="modal-footer"><button type="button" class="btn-secondary" onclick="closeAuditDetailModal()">' +
|
||||
esc(auditT('common.close', null, '关闭')) + '</button></div>' +
|
||||
'</div>';
|
||||
document.body.appendChild(overlay);
|
||||
const chatBtn = overlay.querySelector('.audit-open-chat-btn');
|
||||
if (chatBtn) {
|
||||
chatBtn.addEventListener('click', function () {
|
||||
auditOpenConversationChat(chatBtn.getAttribute('data-conversation-id'));
|
||||
deferModalContent(function () {
|
||||
overlay.innerHTML =
|
||||
'<div class="modal-content" style="max-width: 720px;">' +
|
||||
'<div class="modal-header">' +
|
||||
'<h2>' + esc(auditT('settingsAudit.detailTitle', null, '审计详情')) + '</h2>' +
|
||||
'<span class="modal-close" onclick="closeAuditDetailModal()">×</span>' +
|
||||
'</div>' +
|
||||
'<div class="modal-body audit-detail-body">' +
|
||||
'<p><strong>' + esc(auditT('settingsAudit.detailTime', null, '时间')) + ':</strong> ' + esc(formatAuditTime(log.createdAt)) + '</p>' +
|
||||
'<p><strong>' + esc(auditT('settingsAudit.detailCategory', null, '类别')) + ':</strong> ' + catAction + '</p>' +
|
||||
'<p><strong>' + esc(auditT('settingsAudit.detailResult', null, '结果')) + ':</strong> ' + esc(log.result || '') + '</p>' +
|
||||
'<p><strong>' + esc(auditT('settingsAudit.detailMessage', null, '说明')) + ':</strong> ' + esc(log.message || '') + '</p>' +
|
||||
(log.clientIp ? '<p><strong>IP:</strong> ' + esc(log.clientIp) + '</p>' : '') +
|
||||
(log.sessionHint ? '<p><strong>' + esc(auditT('settingsAudit.detailSession', null, '会话')) + ':</strong> ' + esc(log.sessionHint) + '</p>' : '') +
|
||||
(log.userAgent ? '<p><strong>UA:</strong> ' + esc(log.userAgent) + '</p>' : '') +
|
||||
auditResourceMeta(log) +
|
||||
(detail ? '<pre class="audit-detail-pre">' + esc(detail) + '</pre>' : '') +
|
||||
'</div>' +
|
||||
'<div class="modal-footer"><button type="button" class="btn-secondary" onclick="closeAuditDetailModal()">' +
|
||||
esc(auditT('common.close', null, '关闭')) + '</button></div>' +
|
||||
'</div>';
|
||||
const chatBtn = overlay.querySelector('.audit-open-chat-btn');
|
||||
if (chatBtn) {
|
||||
chatBtn.addEventListener('click', function () {
|
||||
auditOpenConversationChat(chatBtn.getAttribute('data-conversation-id'));
|
||||
});
|
||||
}
|
||||
overlay.addEventListener('click', function (ev) {
|
||||
if (ev.target === overlay) closeAuditDetailModal();
|
||||
});
|
||||
}
|
||||
overlay.addEventListener('click', function (ev) {
|
||||
if (ev.target === overlay) closeAuditDetailModal();
|
||||
});
|
||||
} catch (e) {
|
||||
closeAuditDetailModal();
|
||||
if (typeof showToast === 'function') {
|
||||
showToast(e.message || String(e), 'error');
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ function showLoginOverlay(message = '') {
|
||||
if (!overlay) {
|
||||
return;
|
||||
}
|
||||
overlay.style.display = 'flex';
|
||||
openAppModal('login-overlay', { focus: false });
|
||||
if (errorBox) {
|
||||
if (message) {
|
||||
errorBox.textContent = message;
|
||||
@@ -82,7 +82,7 @@ function showLoginOverlay(message = '') {
|
||||
errorBox.style.display = 'none';
|
||||
}
|
||||
}
|
||||
setTimeout(() => {
|
||||
setTimeout(function () {
|
||||
if (passwordInput) {
|
||||
passwordInput.focus();
|
||||
}
|
||||
@@ -93,9 +93,7 @@ function hideLoginOverlay() {
|
||||
const overlay = document.getElementById('login-overlay');
|
||||
const errorBox = document.getElementById('login-error');
|
||||
const passwordInput = document.getElementById('login-password');
|
||||
if (overlay) {
|
||||
overlay.style.display = 'none';
|
||||
}
|
||||
closeAppModal('login-overlay');
|
||||
if (errorBox) {
|
||||
errorBox.textContent = '';
|
||||
errorBox.style.display = 'none';
|
||||
|
||||
+5
-5
@@ -478,7 +478,7 @@
|
||||
const content = document.getElementById('c2-modal-content');
|
||||
if (!content || !modal) return;
|
||||
|
||||
modal.style.display = 'flex';
|
||||
openAppModal(modal);
|
||||
content.innerHTML = `
|
||||
<div class="c2-modal-header">
|
||||
<h3>${escapeHtml(c2t('c2.listeners.modalCreateTitle'))}</h3>
|
||||
@@ -635,7 +635,7 @@
|
||||
const content = document.getElementById('c2-modal-content');
|
||||
if (!content || !modal) return;
|
||||
|
||||
modal.style.display = 'flex';
|
||||
openAppModal(modal);
|
||||
content.innerHTML = `
|
||||
<div class="c2-modal-header">
|
||||
<h3>${escapeHtml(c2t('c2.listeners.editTitle'))}</h3>
|
||||
@@ -2376,7 +2376,7 @@
|
||||
<button class="btn-secondary" onclick="C2.closeModal()">${escapeHtml(c2t('common.close'))}</button>
|
||||
</div>
|
||||
`;
|
||||
modal.style.display = 'flex';
|
||||
openAppModal(modal);
|
||||
};
|
||||
|
||||
const local = C2.tasks.find(x => x.id === id);
|
||||
@@ -2920,7 +2920,7 @@
|
||||
<button class="btn-primary" onclick="C2.createProfile()">${escapeHtml(c2t('c2.profiles.submitCreate'))}</button>
|
||||
</div>
|
||||
`;
|
||||
modal.style.display = 'flex';
|
||||
openAppModal(modal);
|
||||
};
|
||||
|
||||
C2.createProfile = function() {
|
||||
@@ -2981,10 +2981,10 @@
|
||||
C2.closeModal = function() {
|
||||
const modal = document.getElementById('c2-modal');
|
||||
if (modal) {
|
||||
modal.style.display = 'none';
|
||||
const modalBox = modal.querySelector('.c2-modal');
|
||||
if (modalBox) modalBox.classList.remove('c2-modal--wide');
|
||||
}
|
||||
closeAppModal('c2-modal');
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
|
||||
+12
-11
@@ -1002,7 +1002,7 @@ async function openChatFilesEdit(relativePath) {
|
||||
const modal = document.getElementById('chat-files-edit-modal');
|
||||
if (pathEl) pathEl.textContent = relativePath;
|
||||
if (ta) ta.value = '';
|
||||
if (modal) modal.style.display = 'block';
|
||||
openAppModal('chat-files-edit-modal', { focus: false });
|
||||
|
||||
try {
|
||||
const res = await apiFetch('/api/chat-uploads/content?path=' + encodeURIComponent(relativePath));
|
||||
@@ -1017,16 +1017,19 @@ async function openChatFilesEdit(relativePath) {
|
||||
throw new Error(errText || res.status);
|
||||
}
|
||||
const data = await res.json();
|
||||
if (ta) ta.value = data.content != null ? String(data.content) : '';
|
||||
const content = data.content != null ? String(data.content) : '';
|
||||
deferModalContent(() => {
|
||||
if (ta) ta.value = content;
|
||||
ta?.focus();
|
||||
});
|
||||
} catch (e) {
|
||||
if (modal) modal.style.display = 'none';
|
||||
closeAppModal('chat-files-edit-modal');
|
||||
alert(chatFilesAlertMessage(e && e.message));
|
||||
}
|
||||
}
|
||||
|
||||
function closeChatFilesEditModal() {
|
||||
const modal = document.getElementById('chat-files-edit-modal');
|
||||
if (modal) modal.style.display = 'none';
|
||||
closeAppModal('chat-files-edit-modal');
|
||||
chatFilesEditRelativePath = '';
|
||||
}
|
||||
|
||||
@@ -1060,7 +1063,7 @@ function openChatFilesRename(relativePath, currentName) {
|
||||
input.value = currentName || '';
|
||||
input.select();
|
||||
}
|
||||
if (modal) modal.style.display = 'flex';
|
||||
if (modal) openAppModal(modal);
|
||||
if (modal && typeof window.applyTranslations === 'function') {
|
||||
window.applyTranslations(modal);
|
||||
}
|
||||
@@ -1068,8 +1071,7 @@ function openChatFilesRename(relativePath, currentName) {
|
||||
}
|
||||
|
||||
function closeChatFilesRenameModal() {
|
||||
const modal = document.getElementById('chat-files-rename-modal');
|
||||
if (modal) modal.style.display = 'none';
|
||||
closeAppModal('chat-files-rename-modal');
|
||||
const hint = document.getElementById('chat-files-rename-path-hint');
|
||||
if (hint) hint.textContent = '';
|
||||
chatFilesRenameRelativePath = '';
|
||||
@@ -1106,7 +1108,7 @@ function openChatFilesMkdirModal() {
|
||||
const p = chatFilesBrowsePath.join('/');
|
||||
if (hint) hint.textContent = p ? ('chat_uploads/' + p) : 'chat_uploads';
|
||||
if (input) input.value = '';
|
||||
if (modal) modal.style.display = 'flex';
|
||||
if (modal) openAppModal(modal);
|
||||
if (modal && typeof window.applyTranslations === 'function') {
|
||||
window.applyTranslations(modal);
|
||||
}
|
||||
@@ -1116,8 +1118,7 @@ function openChatFilesMkdirModal() {
|
||||
}
|
||||
|
||||
function closeChatFilesMkdirModal() {
|
||||
const modal = document.getElementById('chat-files-mkdir-modal');
|
||||
if (modal) modal.style.display = 'none';
|
||||
closeAppModal('chat-files-mkdir-modal');
|
||||
const input = document.getElementById('chat-files-mkdir-input');
|
||||
if (input) input.value = '';
|
||||
}
|
||||
|
||||
+27
-49
@@ -2535,10 +2535,17 @@ async function batchUpdateButtonToolNames(buttonsContainer, executionIds) {
|
||||
// 显示MCP调用详情
|
||||
async function showMCPDetail(executionId) {
|
||||
try {
|
||||
openAppModal('mcp-detail-modal', { focus: false });
|
||||
const response = await apiFetch(`/api/monitor/execution/${executionId}`);
|
||||
const exec = await response.json();
|
||||
|
||||
if (response.ok) {
|
||||
|
||||
if (!response.ok) {
|
||||
closeMCPDetail();
|
||||
alert((typeof window.t === 'function' ? window.t('mcpDetailModal.getDetailFailed') : '获取详情失败') + ': ' + (exec.error || (typeof window.t === 'function' ? window.t('mcpDetailModal.unknown') : '未知错误')));
|
||||
return;
|
||||
}
|
||||
|
||||
deferModalContent(function () {
|
||||
// 填充模态框内容
|
||||
document.getElementById('detail-tool-name').textContent = exec.toolName || (typeof window.t === 'function' ? window.t('mcpDetailModal.unknown') : 'Unknown');
|
||||
document.getElementById('detail-execution-id').textContent = exec.id || 'N/A';
|
||||
@@ -2645,20 +2652,16 @@ async function showMCPDetail(executionId) {
|
||||
delete abortBtn.dataset.execId;
|
||||
}
|
||||
}
|
||||
|
||||
// 显示模态框
|
||||
document.getElementById('mcp-detail-modal').style.display = 'block';
|
||||
} else {
|
||||
alert((typeof window.t === 'function' ? window.t('mcpDetailModal.getDetailFailed') : '获取详情失败') + ': ' + (exec.error || (typeof window.t === 'function' ? window.t('mcpDetailModal.unknown') : '未知错误')));
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
closeMCPDetail();
|
||||
alert((typeof window.t === 'function' ? window.t('mcpDetailModal.getDetailFailed') : '获取详情失败') + ': ' + error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// 关闭MCP详情模态框
|
||||
function closeMCPDetail() {
|
||||
document.getElementById('mcp-detail-modal').style.display = 'none';
|
||||
closeAppModal('mcp-detail-modal');
|
||||
}
|
||||
|
||||
/** 从详情模态框触发:取消当前进行中的 MCP 工具调用 */
|
||||
@@ -2682,18 +2685,12 @@ function openMcpToolAbortModal(executionId, options = {}) {
|
||||
if (ta) {
|
||||
ta.value = '';
|
||||
}
|
||||
const m = document.getElementById('mcp-tool-abort-modal');
|
||||
if (m) {
|
||||
m.style.display = 'block';
|
||||
}
|
||||
openAppModal('mcp-tool-abort-modal');
|
||||
}
|
||||
|
||||
function closeMcpToolAbortModal() {
|
||||
window.__mcpToolAbortContext = null;
|
||||
const m = document.getElementById('mcp-tool-abort-modal');
|
||||
if (m) {
|
||||
m.style.display = 'none';
|
||||
}
|
||||
closeAppModal('mcp-tool-abort-modal');
|
||||
}
|
||||
|
||||
async function submitMcpToolAbortModal() {
|
||||
@@ -3125,7 +3122,7 @@ async function loadConversation(conversationId) {
|
||||
|
||||
// 如果攻击链模态框打开且显示的不是当前对话,关闭它
|
||||
const attackChainModal = document.getElementById('attack-chain-modal');
|
||||
if (attackChainModal && attackChainModal.style.display === 'block') {
|
||||
if (attackChainModal && isAppModalOpen('attack-chain-modal')) {
|
||||
if (currentAttackChainConversationId !== conversationId) {
|
||||
closeAttackChainModal();
|
||||
}
|
||||
@@ -3415,7 +3412,7 @@ async function deleteConversation(conversationId, skipConfirm = false) {
|
||||
|
||||
// 批量管理弹窗打开时,同步刷新弹窗内列表
|
||||
const batchModal = document.getElementById('batch-manage-modal');
|
||||
if (batchModal && batchModal.style.display === 'flex') {
|
||||
if (batchModal && isAppModalOpen('batch-manage-modal')) {
|
||||
allConversationsForBatch = allConversationsForBatch.filter(c => c.id !== conversationId);
|
||||
updateBatchManageTitle(allConversationsForBatch.length);
|
||||
const searchInput = document.getElementById('batch-search-input');
|
||||
@@ -3522,7 +3519,7 @@ async function showAttackChain(conversationId) {
|
||||
if (isAttackChainLoading(conversationId) && currentAttackChainConversationId === conversationId) {
|
||||
// 如果模态框已经打开且显示的是同一个对话,不重复打开
|
||||
const modal = document.getElementById('attack-chain-modal');
|
||||
if (modal && modal.style.display === 'block') {
|
||||
if (modal && isAppModalOpen('attack-chain-modal')) {
|
||||
console.log('攻击链正在加载中,模态框已打开');
|
||||
return;
|
||||
}
|
||||
@@ -3535,8 +3532,7 @@ async function showAttackChain(conversationId) {
|
||||
return;
|
||||
}
|
||||
|
||||
modal.style.display = 'block';
|
||||
// 打开时立即按当前语言刷新统计(避免红框内仍显示硬编码中文)
|
||||
openAppModal('attack-chain-modal', { focus: false });
|
||||
updateAttackChainStats({ nodes: [], edges: [] });
|
||||
|
||||
// 清空容器
|
||||
@@ -4668,10 +4664,7 @@ function closeNodeDetails() {
|
||||
|
||||
// 关闭攻击链模态框
|
||||
function closeAttackChainModal() {
|
||||
const modal = document.getElementById('attack-chain-modal');
|
||||
if (modal) {
|
||||
modal.style.display = 'none';
|
||||
}
|
||||
closeAppModal('attack-chain-modal');
|
||||
|
||||
// 关闭节点详情
|
||||
closeNodeDetails();
|
||||
@@ -7214,19 +7207,14 @@ async function showBatchManageModal() {
|
||||
updateBatchManageTitle(allConversationsForBatch.length);
|
||||
|
||||
renderBatchConversations();
|
||||
if (modal) {
|
||||
modal.style.display = 'flex';
|
||||
}
|
||||
openAppModal('batch-manage-modal');
|
||||
} catch (error) {
|
||||
console.error('加载对话列表失败:', error);
|
||||
// 错误时使用空数组,不显示错误提示(更友好的用户体验)
|
||||
allConversationsForBatch = [];
|
||||
const modal = document.getElementById('batch-manage-modal');
|
||||
updateBatchManageTitle(0);
|
||||
if (modal) {
|
||||
renderBatchConversations();
|
||||
modal.style.display = 'flex';
|
||||
}
|
||||
renderBatchConversations();
|
||||
openAppModal('batch-manage-modal');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7381,10 +7369,7 @@ async function deleteSelectedConversations() {
|
||||
|
||||
// 关闭批量管理模态框
|
||||
function closeBatchManageModal() {
|
||||
const modal = document.getElementById('batch-manage-modal');
|
||||
if (modal) {
|
||||
modal.style.display = 'none';
|
||||
}
|
||||
closeAppModal('batch-manage-modal');
|
||||
const selectAll = document.getElementById('batch-select-all');
|
||||
if (selectAll) {
|
||||
selectAll.checked = false;
|
||||
@@ -7424,8 +7409,7 @@ function refreshChatPanelI18n() {
|
||||
});
|
||||
}
|
||||
|
||||
const mcpModal = document.getElementById('mcp-detail-modal');
|
||||
if (mcpModal && mcpModal.style.display === 'block') {
|
||||
if (isAppModalOpen('mcp-detail-modal')) {
|
||||
const detailTimeEl = document.getElementById('detail-time');
|
||||
if (detailTimeEl && detailTimeEl.dataset.detailTimeIso) {
|
||||
try {
|
||||
@@ -7447,7 +7431,7 @@ document.addEventListener('languagechange', function () {
|
||||
refreshSystemReadyMessageBubbles();
|
||||
refreshChatPanelI18n();
|
||||
const modal = document.getElementById('batch-manage-modal');
|
||||
if (modal && modal.style.display === 'flex') {
|
||||
if (isAppModalOpen('batch-manage-modal')) {
|
||||
updateBatchManageTitle(allConversationsForBatch.length);
|
||||
}
|
||||
// 侧边栏最近对话等列表的时间戳会随语言变化(24h/12h 等),重新拉列表以统一格式
|
||||
@@ -7482,20 +7466,14 @@ function showCreateGroupModal(andMoveConversation = false) {
|
||||
iconPicker.style.display = 'none';
|
||||
}
|
||||
if (modal) {
|
||||
modal.style.display = 'flex';
|
||||
openAppModal('create-group-modal', { focusEl: input });
|
||||
modal.dataset.moveConversation = andMoveConversation ? 'true' : 'false';
|
||||
if (input) {
|
||||
setTimeout(() => input.focus(), 100);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 关闭创建分组模态框
|
||||
function closeCreateGroupModal() {
|
||||
const modal = document.getElementById('create-group-modal');
|
||||
if (modal) {
|
||||
modal.style.display = 'none';
|
||||
}
|
||||
closeAppModal('create-group-modal');
|
||||
const input = document.getElementById('create-group-name-input');
|
||||
if (input) {
|
||||
input.value = '';
|
||||
|
||||
@@ -344,7 +344,9 @@ function showFofaParseModal(nlText, parsed) {
|
||||
const modal = document.createElement('div');
|
||||
modal.id = 'fofa-parse-modal';
|
||||
modal.className = 'modal';
|
||||
modal.style.display = 'block';
|
||||
document.body.appendChild(modal);
|
||||
openAppModal(modal, { focus: false });
|
||||
deferModalContent(function () {
|
||||
modal.innerHTML = `
|
||||
<div class="modal-content" style="max-width: 900px;">
|
||||
<div class="modal-header">
|
||||
@@ -384,24 +386,24 @@ function showFofaParseModal(nlText, parsed) {
|
||||
</div>
|
||||
`;
|
||||
|
||||
document.body.appendChild(modal);
|
||||
|
||||
const queryTextarea = document.getElementById('fofa-parse-query');
|
||||
if (queryTextarea) {
|
||||
queryTextarea.value = (parsed?.query || '').trim();
|
||||
setTimeout(() => {
|
||||
try { queryTextarea.focus(); } catch (e) { /* ignore */ }
|
||||
}, 0);
|
||||
queryTextarea.focus();
|
||||
}
|
||||
|
||||
const close = () => modal.remove();
|
||||
modal.addEventListener('click', (e) => {
|
||||
const close = function () {
|
||||
closeAppModal(modal);
|
||||
modal.remove();
|
||||
syncAppModalBodyLock();
|
||||
};
|
||||
modal.addEventListener('click', function (e) {
|
||||
if (e.target === modal) close();
|
||||
});
|
||||
document.getElementById('fofa-parse-modal-close')?.addEventListener('click', close);
|
||||
document.getElementById('fofa-parse-cancel')?.addEventListener('click', close);
|
||||
|
||||
const applyToQuery = (run) => {
|
||||
const applyToQuery = function (run) {
|
||||
const els = getFofaFormElements();
|
||||
const q = (queryTextarea?.value || '').trim();
|
||||
if (!q) {
|
||||
@@ -435,6 +437,7 @@ function showFofaParseModal(nlText, parsed) {
|
||||
}
|
||||
};
|
||||
document.addEventListener('keydown', onKey);
|
||||
});
|
||||
}
|
||||
|
||||
function setFofaMeta(text) {
|
||||
@@ -1091,8 +1094,13 @@ function showCellDetailModal(field, fullText) {
|
||||
`;
|
||||
|
||||
document.body.appendChild(modal);
|
||||
openAppModal(modal);
|
||||
|
||||
const close = () => modal.remove();
|
||||
const close = function () {
|
||||
closeAppModal(modal);
|
||||
modal.remove();
|
||||
syncAppModalBodyLock();
|
||||
};
|
||||
modal.addEventListener('click', (e) => {
|
||||
if (e.target === modal) close();
|
||||
});
|
||||
|
||||
+24
-20
@@ -905,25 +905,32 @@ function showAddKnowledgeItemModal() {
|
||||
document.getElementById('knowledge-item-category').value = '';
|
||||
document.getElementById('knowledge-item-title').value = '';
|
||||
document.getElementById('knowledge-item-content').value = '';
|
||||
document.getElementById('knowledge-item-modal').style.display = 'block';
|
||||
openAppModal('knowledge-item-modal');
|
||||
}
|
||||
|
||||
// 编辑知识项
|
||||
async function editKnowledgeItem(id) {
|
||||
try {
|
||||
currentEditingItemId = id;
|
||||
document.getElementById('knowledge-item-modal-title').textContent = '编辑知识';
|
||||
document.getElementById('knowledge-item-category').value = '';
|
||||
document.getElementById('knowledge-item-title').value = '';
|
||||
document.getElementById('knowledge-item-content').value = '';
|
||||
openAppModal('knowledge-item-modal', { focus: false });
|
||||
const response = await apiFetch(`/api/knowledge/items/${id}`);
|
||||
if (!response.ok) {
|
||||
throw new Error('获取知识项失败');
|
||||
}
|
||||
const item = await response.json();
|
||||
|
||||
currentEditingItemId = id;
|
||||
document.getElementById('knowledge-item-modal-title').textContent = '编辑知识';
|
||||
document.getElementById('knowledge-item-category').value = item.category;
|
||||
document.getElementById('knowledge-item-title').value = item.title;
|
||||
document.getElementById('knowledge-item-content').value = item.content;
|
||||
document.getElementById('knowledge-item-modal').style.display = 'block';
|
||||
deferModalContent(() => {
|
||||
document.getElementById('knowledge-item-category').value = item.category;
|
||||
document.getElementById('knowledge-item-title').value = item.title;
|
||||
document.getElementById('knowledge-item-content').value = item.content;
|
||||
document.getElementById('knowledge-item-title')?.focus();
|
||||
});
|
||||
} catch (error) {
|
||||
closeAppModal('knowledge-item-modal');
|
||||
currentEditingItemId = null;
|
||||
console.error('编辑知识项失败:', error);
|
||||
showNotification('编辑知识项失败: ' + error.message, 'error');
|
||||
}
|
||||
@@ -1232,10 +1239,7 @@ function updateKnowledgeStatsAfterDelete() {
|
||||
|
||||
// 关闭知识项模态框
|
||||
function closeKnowledgeItemModal() {
|
||||
const modal = document.getElementById('knowledge-item-modal');
|
||||
if (modal) {
|
||||
modal.style.display = 'none';
|
||||
}
|
||||
closeAppModal('knowledge-item-modal');
|
||||
|
||||
// 重置编辑状态
|
||||
currentEditingItemId = null;
|
||||
@@ -1786,8 +1790,11 @@ function showRetrievalLogDetailsModal(log, retrievedItems) {
|
||||
document.body.appendChild(modal);
|
||||
}
|
||||
|
||||
// 填充内容
|
||||
const content = document.getElementById('retrieval-log-details-content');
|
||||
if (content) content.innerHTML = '<p style="color:#64748b;margin:0;">…</p>';
|
||||
openAppModal(modal, { focus: false });
|
||||
|
||||
deferModalContent(() => {
|
||||
const timeAgo = getTimeAgo(log.createdAt);
|
||||
const fullTime = formatTime(log.createdAt);
|
||||
|
||||
@@ -1880,16 +1887,12 @@ function showRetrievalLogDetailsModal(log, retrievedItems) {
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
modal.style.display = 'block';
|
||||
});
|
||||
}
|
||||
|
||||
// 关闭检索日志详情模态框
|
||||
function closeRetrievalLogDetailsModal() {
|
||||
const modal = document.getElementById('retrieval-log-details-modal');
|
||||
if (modal) {
|
||||
modal.style.display = 'none';
|
||||
}
|
||||
closeAppModal('retrieval-log-details-modal');
|
||||
}
|
||||
|
||||
// 点击模态框外部关闭
|
||||
@@ -2118,7 +2121,8 @@ function showToastNotification(message, type = 'info') {
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.45;
|
||||
word-wrap: break-word;
|
||||
backdrop-filter: blur(8px);
|
||||
backdrop-filter: none;
|
||||
-webkit-backdrop-filter: none;
|
||||
`;
|
||||
|
||||
toast.innerHTML = `
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
/**
|
||||
* 统一弹窗:先显示遮罩、下一帧再填大段内容,避免与 backdrop 绘制抢主线程。
|
||||
*/
|
||||
(function () {
|
||||
const BODY_LOCK = 'app-modal-open';
|
||||
const LEGACY_BODY_LOCK = 'projects-modal-open';
|
||||
const OVERLAY_SELECTOR =
|
||||
'.projects-modal-overlay, .c2-modal-overlay, .modal, .info-collect-cell-modal, #login-overlay';
|
||||
|
||||
const FLEX_MODAL_IDS = new Set([
|
||||
'role-modal',
|
||||
'skill-modal',
|
||||
'agent-md-modal',
|
||||
'batch-manage-modal',
|
||||
'create-group-modal',
|
||||
'login-overlay',
|
||||
]);
|
||||
|
||||
function resolveEl(idOrEl) {
|
||||
if (!idOrEl) return null;
|
||||
return typeof idOrEl === 'string' ? document.getElementById(idOrEl) : idOrEl;
|
||||
}
|
||||
|
||||
function isElVisible(el) {
|
||||
if (!el) return false;
|
||||
const s = window.getComputedStyle(el);
|
||||
return s.display !== 'none' && s.visibility !== 'hidden';
|
||||
}
|
||||
|
||||
function defaultDisplay(el) {
|
||||
if (el.classList.contains('projects-modal-overlay') || el.classList.contains('c2-modal-overlay')) {
|
||||
return 'flex';
|
||||
}
|
||||
if (el.classList.contains('info-collect-cell-modal')) {
|
||||
return 'flex';
|
||||
}
|
||||
if (FLEX_MODAL_IDS.has(el.id)) {
|
||||
return 'flex';
|
||||
}
|
||||
return 'block';
|
||||
}
|
||||
|
||||
function syncBodyLock() {
|
||||
const anyOpen = Array.from(document.querySelectorAll(OVERLAY_SELECTOR)).some(isElVisible);
|
||||
document.body.classList.toggle(BODY_LOCK, anyOpen);
|
||||
const projectsOpen = Array.from(document.querySelectorAll('.projects-modal-overlay')).some(isElVisible);
|
||||
document.body.classList.toggle(LEGACY_BODY_LOCK, projectsOpen);
|
||||
}
|
||||
|
||||
function openAppModal(idOrEl, opts) {
|
||||
opts = opts || {};
|
||||
const el = resolveEl(idOrEl);
|
||||
if (!el) return null;
|
||||
el.style.display = opts.display || defaultDisplay(el);
|
||||
syncBodyLock();
|
||||
if (opts.focus === false) return el;
|
||||
const sel =
|
||||
opts.focusSelector ||
|
||||
'input.form-input, textarea.form-input, select.form-input, input:not([type="hidden"]):not([disabled]), textarea:not([disabled]), select:not([disabled])';
|
||||
const focusTarget = opts.focusEl || el.querySelector(sel);
|
||||
if (focusTarget) {
|
||||
requestAnimationFrame(function () {
|
||||
focusTarget.focus();
|
||||
});
|
||||
}
|
||||
return el;
|
||||
}
|
||||
|
||||
function closeAppModal(idOrEl) {
|
||||
const el = resolveEl(idOrEl);
|
||||
if (el) el.style.display = 'none';
|
||||
syncBodyLock();
|
||||
return el;
|
||||
}
|
||||
|
||||
function isAppModalOpen(idOrEl) {
|
||||
return isElVisible(resolveEl(idOrEl));
|
||||
}
|
||||
|
||||
/** 双 rAF:等遮罩绘制完成后再写入大段 DOM / 表单 */
|
||||
function deferModalContent(fn) {
|
||||
requestAnimationFrame(function () {
|
||||
requestAnimationFrame(fn);
|
||||
});
|
||||
}
|
||||
|
||||
window.openAppModal = openAppModal;
|
||||
window.closeAppModal = closeAppModal;
|
||||
window.isAppModalOpen = isAppModalOpen;
|
||||
window.deferModalContent = deferModalContent;
|
||||
window.syncAppModalBodyLock = syncBodyLock;
|
||||
})();
|
||||
@@ -944,18 +944,12 @@ function openUserInterruptModal(progressId, conversationId) {
|
||||
if (ta) {
|
||||
ta.value = '';
|
||||
}
|
||||
const m = document.getElementById('user-interrupt-modal');
|
||||
if (m) {
|
||||
m.style.display = 'block';
|
||||
}
|
||||
openAppModal('user-interrupt-modal');
|
||||
}
|
||||
|
||||
function closeUserInterruptModal() {
|
||||
userInterruptModalPending = null;
|
||||
const m = document.getElementById('user-interrupt-modal');
|
||||
if (m) {
|
||||
m.style.display = 'none';
|
||||
}
|
||||
closeAppModal('user-interrupt-modal');
|
||||
}
|
||||
|
||||
async function submitUserInterruptContinue() {
|
||||
|
||||
+70
-65
@@ -12,6 +12,7 @@ let _projectsFetchPromise = null;
|
||||
|
||||
const PROJECT_ACTIVE_KEY = 'cyberstrike.activeProjectId';
|
||||
const PROJECT_DESCRIPTION_MAX_LENGTH = 4000;
|
||||
const PROJECT_NAME_MAX_LENGTH = 200;
|
||||
|
||||
function tp(key, opts) {
|
||||
if (typeof window.t === 'function') return window.t(key, opts);
|
||||
@@ -878,38 +879,52 @@ let _factDetailFact = null;
|
||||
let _projectFactsFilterDebounce = null;
|
||||
|
||||
async function viewProjectFactBody(factKey) {
|
||||
const res = await apiFetch(`/api/projects/${currentProjectId}/facts?fact_key=${encodeURIComponent(factKey)}`);
|
||||
if (!res.ok) return alert(tp('common.loadFailed'));
|
||||
const f = await res.json();
|
||||
_factDetailKey = f.fact_key;
|
||||
_factDetailFact = f;
|
||||
document.getElementById('fact-detail-title').textContent = `[${f.fact_key}]`;
|
||||
const metaParts = [
|
||||
tpFmt('projects.factMetaCategory', `Category: ${f.category}`, { value: f.category }),
|
||||
tpFmt('projects.factMetaConfidence', `Confidence: ${f.confidence}`, { value: f.confidence }),
|
||||
tpFmt('projects.factMetaUpdated', `Updated: ${formatProjectTime(f.updated_at, f.created_at)}`, {
|
||||
time: formatProjectTime(f.updated_at, f.created_at),
|
||||
}),
|
||||
];
|
||||
if (f.related_vulnerability_id) metaParts.push(tpFmt('projects.factMetaRelatedVuln', `Related vulnerability: ${f.related_vulnerability_id}`, { value: f.related_vulnerability_id }));
|
||||
if (f.source_conversation_id) metaParts.push(tpFmt('projects.factMetaSourceConversation', `Source conversation: ${f.source_conversation_id}`, { value: f.source_conversation_id }));
|
||||
document.getElementById('fact-detail-meta').textContent = metaParts.join(' · ');
|
||||
document.getElementById('fact-detail-body').textContent = f.body || tp('projects.emptyBody');
|
||||
document.getElementById('fact-detail-title').textContent = factKey;
|
||||
document.getElementById('fact-detail-meta').textContent = '…';
|
||||
document.getElementById('fact-detail-body').textContent = '';
|
||||
const warnEl = document.getElementById('fact-detail-sparse-warn');
|
||||
if (warnEl) {
|
||||
if (isSparseFactBody(f.category, f.fact_key, f.body)) {
|
||||
warnEl.hidden = false;
|
||||
warnEl.textContent = tp('projects.factSparseWarn');
|
||||
} else {
|
||||
warnEl.hidden = true;
|
||||
warnEl.textContent = '';
|
||||
}
|
||||
warnEl.hidden = true;
|
||||
warnEl.textContent = '';
|
||||
}
|
||||
const linkBtn = document.getElementById('fact-detail-link-vuln-btn');
|
||||
const createBtn = document.getElementById('fact-detail-create-vuln-btn');
|
||||
if (linkBtn) linkBtn.hidden = false;
|
||||
if (createBtn) createBtn.hidden = false;
|
||||
openProjectsOverlay('fact-detail-modal');
|
||||
if (linkBtn) linkBtn.hidden = true;
|
||||
if (createBtn) createBtn.hidden = true;
|
||||
openProjectsOverlay('fact-detail-modal', { focus: false });
|
||||
const res = await apiFetch(`/api/projects/${currentProjectId}/facts?fact_key=${encodeURIComponent(factKey)}`);
|
||||
if (!res.ok) {
|
||||
closeFactDetailModal();
|
||||
return alert(tp('common.loadFailed'));
|
||||
}
|
||||
const f = await res.json();
|
||||
_factDetailKey = f.fact_key;
|
||||
_factDetailFact = f;
|
||||
deferModalContent(() => {
|
||||
document.getElementById('fact-detail-title').textContent = `[${f.fact_key}]`;
|
||||
const metaParts = [
|
||||
tpFmt('projects.factMetaCategory', `Category: ${f.category}`, { value: f.category }),
|
||||
tpFmt('projects.factMetaConfidence', `Confidence: ${f.confidence}`, { value: f.confidence }),
|
||||
tpFmt('projects.factMetaUpdated', `Updated: ${formatProjectTime(f.updated_at, f.created_at)}`, {
|
||||
time: formatProjectTime(f.updated_at, f.created_at),
|
||||
}),
|
||||
];
|
||||
if (f.related_vulnerability_id) metaParts.push(tpFmt('projects.factMetaRelatedVuln', `Related vulnerability: ${f.related_vulnerability_id}`, { value: f.related_vulnerability_id }));
|
||||
if (f.source_conversation_id) metaParts.push(tpFmt('projects.factMetaSourceConversation', `Source conversation: ${f.source_conversation_id}`, { value: f.source_conversation_id }));
|
||||
document.getElementById('fact-detail-meta').textContent = metaParts.join(' · ');
|
||||
document.getElementById('fact-detail-body').textContent = f.body || tp('projects.emptyBody');
|
||||
if (warnEl) {
|
||||
if (isSparseFactBody(f.category, f.fact_key, f.body)) {
|
||||
warnEl.hidden = false;
|
||||
warnEl.textContent = tp('projects.factSparseWarn');
|
||||
} else {
|
||||
warnEl.hidden = true;
|
||||
warnEl.textContent = '';
|
||||
}
|
||||
}
|
||||
if (linkBtn) linkBtn.hidden = false;
|
||||
if (createBtn) createBtn.hidden = false;
|
||||
});
|
||||
}
|
||||
|
||||
function editFactFromDetail() {
|
||||
@@ -1164,41 +1179,16 @@ async function viewFactsForVulnerability(vulnId) {
|
||||
else loadProjectFacts();
|
||||
}
|
||||
|
||||
function openProjectsOverlay(id) {
|
||||
const el = document.getElementById(id);
|
||||
if (!el) return;
|
||||
el.style.display = 'flex';
|
||||
syncProjectsModalBodyLock();
|
||||
const focusTarget = el.querySelector('input.form-input, textarea.form-input, select.form-input');
|
||||
if (focusTarget) {
|
||||
setTimeout(() => focusTarget.focus(), 80);
|
||||
}
|
||||
function openProjectsOverlay(id, opts) {
|
||||
openAppModal(id, opts);
|
||||
}
|
||||
|
||||
function isProjectsOverlayVisible(id) {
|
||||
const el = document.getElementById(id);
|
||||
if (!el) return false;
|
||||
const style = window.getComputedStyle(el);
|
||||
return style.display !== 'none' && style.visibility !== 'hidden';
|
||||
}
|
||||
|
||||
function hasVisibleProjectsOverlay() {
|
||||
const overlays = document.querySelectorAll('.projects-modal-overlay');
|
||||
return Array.from(overlays).some((el) => {
|
||||
const style = window.getComputedStyle(el);
|
||||
return style.display !== 'none' && style.visibility !== 'hidden';
|
||||
});
|
||||
}
|
||||
|
||||
function syncProjectsModalBodyLock() {
|
||||
if (hasVisibleProjectsOverlay()) document.body.classList.add('projects-modal-open');
|
||||
else document.body.classList.remove('projects-modal-open');
|
||||
return isAppModalOpen(id);
|
||||
}
|
||||
|
||||
function closeProjectsOverlay(id) {
|
||||
const el = document.getElementById(id);
|
||||
if (el) el.style.display = 'none';
|
||||
syncProjectsModalBodyLock();
|
||||
closeAppModal(id);
|
||||
}
|
||||
|
||||
function showNewProjectModal() {
|
||||
@@ -1222,6 +1212,11 @@ async function showEditProjectModal(projectId) {
|
||||
if (sub) sub.textContent = tp('projects.modalEditSubtitle');
|
||||
const submitBtn = document.getElementById('project-modal-submit-btn');
|
||||
if (submitBtn) submitBtn.textContent = tp('projects.saveChanges');
|
||||
const nameEl = document.getElementById('project-modal-name');
|
||||
const descEl = document.getElementById('project-modal-description');
|
||||
if (nameEl) nameEl.value = '';
|
||||
if (descEl) descEl.value = '';
|
||||
openProjectsOverlay('project-modal', { focus: false });
|
||||
let p = findProjectById(projectId);
|
||||
if (!p) {
|
||||
try {
|
||||
@@ -1229,15 +1224,19 @@ async function showEditProjectModal(projectId) {
|
||||
if (!res.ok) throw new Error(tp('projects.projectNotFound'));
|
||||
p = await res.json();
|
||||
} catch (e) {
|
||||
closeProjectModal();
|
||||
alert(e.message || tp('projects.projectNotFound'));
|
||||
window._projectModalEditId = null;
|
||||
return;
|
||||
}
|
||||
}
|
||||
document.getElementById('project-modal-name').value = p.name || '';
|
||||
document.getElementById('project-modal-description').value = p.description || '';
|
||||
openProjectsOverlay('project-modal');
|
||||
setTimeout(() => document.getElementById('project-modal-name')?.focus(), 0);
|
||||
const name = (p.name || '').slice(0, PROJECT_NAME_MAX_LENGTH);
|
||||
const description = clampProjectDescription(p.description || '');
|
||||
deferModalContent(() => {
|
||||
if (nameEl) nameEl.value = name;
|
||||
if (descEl) descEl.value = description;
|
||||
nameEl?.focus();
|
||||
});
|
||||
}
|
||||
|
||||
/** 从对话区「选择项目」面板打开新建项目,创建成功后自动绑定当前对话 */
|
||||
@@ -1248,7 +1247,7 @@ function showNewProjectModalFromChat() {
|
||||
}
|
||||
|
||||
async function saveProjectModal() {
|
||||
const name = document.getElementById('project-modal-name').value.trim();
|
||||
const name = document.getElementById('project-modal-name').value.trim().slice(0, PROJECT_NAME_MAX_LENGTH);
|
||||
if (!name) return alert(tp('projects.enterProjectName'));
|
||||
const body = {
|
||||
name,
|
||||
@@ -1541,14 +1540,20 @@ function showAddFactModal() {
|
||||
|
||||
async function showEditFactModal(factKey) {
|
||||
if (!currentProjectId) return alert(tp('projects.selectProjectFirst'));
|
||||
resetFactModalForm();
|
||||
openProjectsOverlay('fact-modal', { focus: false });
|
||||
const res = await apiFetch(
|
||||
`/api/projects/${currentProjectId}/facts?fact_key=${encodeURIComponent(factKey)}`,
|
||||
);
|
||||
if (!res.ok) return alert(tp('projects.loadFactFailed'));
|
||||
if (!res.ok) {
|
||||
closeFactModal();
|
||||
return alert(tp('projects.loadFactFailed'));
|
||||
}
|
||||
const f = await res.json();
|
||||
resetFactModalForm();
|
||||
fillFactModalForm(f);
|
||||
openProjectsOverlay('fact-modal');
|
||||
deferModalContent(() => {
|
||||
fillFactModalForm(f);
|
||||
document.getElementById('fact-modal-key')?.focus();
|
||||
});
|
||||
}
|
||||
|
||||
function closeFactModal() {
|
||||
|
||||
@@ -1112,7 +1112,7 @@ async function showAddRoleModal() {
|
||||
// 确保统计信息正确更新(显示0/108)
|
||||
updateRoleToolsStats();
|
||||
|
||||
modal.style.display = 'flex';
|
||||
openAppModal('role-modal');
|
||||
}
|
||||
|
||||
// 编辑角色
|
||||
@@ -1274,15 +1274,16 @@ async function editRole(roleName) {
|
||||
}
|
||||
}
|
||||
|
||||
modal.style.display = 'flex';
|
||||
openAppModal('role-modal');
|
||||
}
|
||||
|
||||
// 关闭角色模态框
|
||||
function closeRoleModal() {
|
||||
const modal = document.getElementById('role-modal');
|
||||
if (modal) {
|
||||
modal.style.display = 'none';
|
||||
}
|
||||
closeAppModal('role-modal');
|
||||
}
|
||||
|
||||
function closeRoleSelectModal() {
|
||||
closeAppModal('role-select-modal');
|
||||
}
|
||||
|
||||
// 获取所有选中的工具(包括未在MCP管理中启用的工具)
|
||||
@@ -1634,6 +1635,7 @@ if (typeof window !== 'undefined') {
|
||||
window.getCurrentRole = getCurrentRole;
|
||||
window.toggleRoleSelectionPanel = toggleRoleSelectionPanel;
|
||||
window.closeRoleSelectionPanel = closeRoleSelectionPanel;
|
||||
window.closeRoleSelectModal = closeRoleSelectModal;
|
||||
window.filterRoleToolsByStatus = filterRoleToolsByStatus;
|
||||
window.currentSelectedRole = getCurrentRole();
|
||||
|
||||
|
||||
+14
-19
@@ -2096,47 +2096,42 @@ function showAddExternalMCPModal() {
|
||||
document.getElementById('external-mcp-json-error').style.display = 'none';
|
||||
document.getElementById('external-mcp-json-error').textContent = '';
|
||||
document.getElementById('external-mcp-json').classList.remove('error');
|
||||
document.getElementById('external-mcp-modal').style.display = 'block';
|
||||
openAppModal('external-mcp-modal');
|
||||
}
|
||||
|
||||
// 关闭外部MCP模态框
|
||||
function closeExternalMCPModal() {
|
||||
document.getElementById('external-mcp-modal').style.display = 'none';
|
||||
closeAppModal('external-mcp-modal');
|
||||
currentEditingMCPName = null;
|
||||
}
|
||||
|
||||
// 编辑外部MCP
|
||||
async function editExternalMCP(name) {
|
||||
try {
|
||||
currentEditingMCPName = name;
|
||||
document.getElementById('external-mcp-modal-title').textContent = (typeof window.t === 'function' ? window.t('mcp.editExternalMCP') : '编辑外部MCP');
|
||||
document.getElementById('external-mcp-json').value = '';
|
||||
document.getElementById('external-mcp-json-error').style.display = 'none';
|
||||
document.getElementById('external-mcp-json-error').textContent = '';
|
||||
document.getElementById('external-mcp-json').classList.remove('error');
|
||||
openAppModal('external-mcp-modal', { focus: false });
|
||||
const response = await apiFetch(`/api/external-mcp/${encodeURIComponent(name)}`);
|
||||
if (!response.ok) {
|
||||
throw new Error(typeof window.t === 'function' ? window.t('mcp.getConfigFailed') : '获取外部MCP配置失败');
|
||||
}
|
||||
|
||||
const server = await response.json();
|
||||
currentEditingMCPName = name;
|
||||
|
||||
document.getElementById('external-mcp-modal-title').textContent = (typeof window.t === 'function' ? window.t('mcp.editExternalMCP') : '编辑外部MCP');
|
||||
|
||||
// 将配置转换为对象格式(key为名称)
|
||||
const config = { ...server.config };
|
||||
// 移除tool_count、external_mcp_enable等前端字段,但保留enabled/disabled用于向后兼容
|
||||
delete config.tool_count;
|
||||
delete config.external_mcp_enable;
|
||||
|
||||
// 包装成对象格式:{ "name": { config } }
|
||||
const configObj = {};
|
||||
configObj[name] = config;
|
||||
|
||||
// 格式化JSON
|
||||
const jsonStr = JSON.stringify(configObj, null, 2);
|
||||
document.getElementById('external-mcp-json').value = jsonStr;
|
||||
document.getElementById('external-mcp-json-error').style.display = 'none';
|
||||
document.getElementById('external-mcp-json-error').textContent = '';
|
||||
document.getElementById('external-mcp-json').classList.remove('error');
|
||||
|
||||
document.getElementById('external-mcp-modal').style.display = 'block';
|
||||
deferModalContent(() => {
|
||||
document.getElementById('external-mcp-json').value = jsonStr;
|
||||
document.getElementById('external-mcp-json')?.focus();
|
||||
});
|
||||
} catch (error) {
|
||||
closeExternalMCPModal();
|
||||
console.error('编辑外部MCP失败:', error);
|
||||
alert((typeof window.t === 'function' ? window.t('mcp.operationFailed') : '编辑失败') + ': ' + error.message);
|
||||
}
|
||||
|
||||
+40
-40
@@ -40,7 +40,7 @@ function shouldSkipSkillsAutoRefresh() {
|
||||
}
|
||||
|
||||
const modal = document.getElementById('skill-modal');
|
||||
if (modal && modal.style.display === 'flex') {
|
||||
if (modal && isAppModalOpen('skill-modal')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -465,7 +465,7 @@ function showAddSkillModal() {
|
||||
const addTa = document.getElementById('skill-content-add');
|
||||
if (addTa) addTa.value = '';
|
||||
|
||||
modal.style.display = 'flex';
|
||||
openAppModal('skill-modal');
|
||||
}
|
||||
|
||||
function skillPackagePathDepth(path) {
|
||||
@@ -555,6 +555,22 @@ async function selectSkillPackageFile(skillId, path, opts) {
|
||||
// 编辑skill
|
||||
async function editSkill(skillId) {
|
||||
wireSkillModalOnce();
|
||||
const modal = document.getElementById('skill-modal');
|
||||
if (!modal) return;
|
||||
skillModalAddMode = false;
|
||||
skillFileDirty = false;
|
||||
skillActivePath = 'SKILL.md';
|
||||
const pkg = document.getElementById('skill-package-editor');
|
||||
const addEd = document.getElementById('skill-add-editor');
|
||||
if (pkg) pkg.style.display = 'block';
|
||||
if (addEd) addEd.style.display = 'none';
|
||||
document.getElementById('skill-modal-title').textContent = _t('skills.editSkill');
|
||||
document.getElementById('skill-name').value = '';
|
||||
document.getElementById('skill-name').disabled = true;
|
||||
document.getElementById('skill-description').value = '';
|
||||
const ta = document.getElementById('skill-content');
|
||||
if (ta) ta.value = '';
|
||||
openAppModal('skill-modal', { focus: false });
|
||||
try {
|
||||
const [detailRes, filesRes] = await Promise.all([
|
||||
apiFetch(`/api/skills/${encodeURIComponent(skillId)}?depth=full`),
|
||||
@@ -565,39 +581,24 @@ async function editSkill(skillId) {
|
||||
}
|
||||
const data = await detailRes.json();
|
||||
const skill = data.skill;
|
||||
|
||||
const modal = document.getElementById('skill-modal');
|
||||
if (!modal) return;
|
||||
|
||||
skillModalAddMode = false;
|
||||
skillFileDirty = false;
|
||||
skillActivePath = 'SKILL.md';
|
||||
const pkg = document.getElementById('skill-package-editor');
|
||||
const addEd = document.getElementById('skill-add-editor');
|
||||
if (pkg) pkg.style.display = 'block';
|
||||
if (addEd) addEd.style.display = 'none';
|
||||
|
||||
document.getElementById('skill-modal-title').textContent = _t('skills.editSkill');
|
||||
document.getElementById('skill-name').value = skill.id || skillId;
|
||||
document.getElementById('skill-name').disabled = true;
|
||||
document.getElementById('skill-description').value = skill.description || '';
|
||||
|
||||
let files = [];
|
||||
if (filesRes.ok) {
|
||||
const fd = await filesRes.json();
|
||||
skillPackageFiles = fd.files || [];
|
||||
} else {
|
||||
skillPackageFiles = [];
|
||||
files = fd.files || [];
|
||||
}
|
||||
renderSkillPackageTree();
|
||||
|
||||
const ta = document.getElementById('skill-content');
|
||||
if (ta) ta.value = skill.content || '';
|
||||
const hint = document.getElementById('skill-body-hint-edit');
|
||||
if (hint) hint.style.display = 'block';
|
||||
|
||||
currentEditingSkillName = skillId;
|
||||
modal.style.display = 'flex';
|
||||
deferModalContent(() => {
|
||||
document.getElementById('skill-name').value = skill.id || skillId;
|
||||
document.getElementById('skill-description').value = skill.description || '';
|
||||
skillPackageFiles = files;
|
||||
renderSkillPackageTree();
|
||||
if (ta) ta.value = skill.content || '';
|
||||
const hint = document.getElementById('skill-body-hint-edit');
|
||||
if (hint) hint.style.display = 'block';
|
||||
document.getElementById('skill-name')?.focus();
|
||||
});
|
||||
} catch (error) {
|
||||
closeSkillModal();
|
||||
console.error('加载skill详情失败:', error);
|
||||
showNotification(_t('skills.loadDetailFailed') + ': ' + error.message, 'error');
|
||||
}
|
||||
@@ -659,7 +660,7 @@ async function viewSkill(skillId) {
|
||||
</div>
|
||||
`;
|
||||
document.body.appendChild(modal);
|
||||
modal.style.display = 'flex';
|
||||
openAppModal(modal);
|
||||
|
||||
const close = () => closeSkillViewModal();
|
||||
modal.querySelectorAll('[data-skill-view-close]').forEach(el => el.addEventListener('click', close));
|
||||
@@ -691,23 +692,22 @@ async function viewSkill(skillId) {
|
||||
|
||||
// 关闭查看模态框
|
||||
function closeSkillViewModal() {
|
||||
closeAppModal('skill-view-modal');
|
||||
const modal = document.getElementById('skill-view-modal');
|
||||
if (modal) {
|
||||
modal.remove();
|
||||
syncAppModalBodyLock();
|
||||
}
|
||||
}
|
||||
|
||||
// 关闭skill模态框
|
||||
function closeSkillModal() {
|
||||
const modal = document.getElementById('skill-modal');
|
||||
if (modal) {
|
||||
modal.style.display = 'none';
|
||||
currentEditingSkillName = null;
|
||||
skillModalAddMode = true;
|
||||
skillFileDirty = false;
|
||||
skillPackageFiles = [];
|
||||
skillActivePath = 'SKILL.md';
|
||||
}
|
||||
closeAppModal('skill-modal');
|
||||
currentEditingSkillName = null;
|
||||
skillModalAddMode = true;
|
||||
skillFileDirty = false;
|
||||
skillPackageFiles = [];
|
||||
skillActivePath = 'SKILL.md';
|
||||
}
|
||||
|
||||
// 保存skill
|
||||
|
||||
+18
-25
@@ -914,18 +914,14 @@ async function showBatchImportModal() {
|
||||
}
|
||||
}
|
||||
await refreshBatchProjectSelectOptions();
|
||||
|
||||
modal.style.display = 'block';
|
||||
input.focus();
|
||||
|
||||
openAppModal('batch-import-modal', { focusEl: input });
|
||||
}
|
||||
}
|
||||
|
||||
// 关闭新建任务模态框
|
||||
function closeBatchImportModal() {
|
||||
const modal = document.getElementById('batch-import-modal');
|
||||
if (modal) {
|
||||
modal.style.display = 'none';
|
||||
}
|
||||
closeAppModal('batch-import-modal');
|
||||
}
|
||||
|
||||
function handleBatchScheduleModeChange() {
|
||||
@@ -1350,7 +1346,13 @@ async function showBatchQueueDetail(queueId) {
|
||||
const addTaskBtn = document.getElementById('batch-queue-add-task-btn');
|
||||
|
||||
if (!modal || !content) return;
|
||||
|
||||
|
||||
const alreadyOpen = isAppModalOpen('batch-queue-detail-modal');
|
||||
if (!alreadyOpen) {
|
||||
if (content) content.innerHTML = '<p style="color:#64748b;margin:0;">…</p>';
|
||||
openAppModal('batch-queue-detail-modal', { focus: false });
|
||||
}
|
||||
|
||||
try {
|
||||
// 加载角色列表(如果还未加载)
|
||||
let loadedRoles = [];
|
||||
@@ -1459,6 +1461,7 @@ async function showBatchQueueDetail(queueId) {
|
||||
const sameQueueAsBefore = prevDetailFor === queue.id;
|
||||
const savedTechDetailsOpen = sameQueueAsBefore && !!(prevTechDetails && prevTechDetails.open);
|
||||
|
||||
deferModalContent(function () {
|
||||
content.innerHTML = `
|
||||
<div class="batch-queue-detail-layout" data-bq-detail-for="${escapeHtml(queue.id)}">
|
||||
<section class="batch-queue-detail-hero">
|
||||
@@ -1529,8 +1532,7 @@ async function showBatchQueueDetail(queueId) {
|
||||
if (newTechDetails && savedTechDetailsOpen) {
|
||||
newTechDetails.open = true;
|
||||
}
|
||||
|
||||
modal.style.display = 'block';
|
||||
});
|
||||
|
||||
// 仅运行中定时拉取详情;其它状态应停止,避免 innerHTML 重绘把 <details> 等 UI 打回默认态
|
||||
if (queue.status === 'running') {
|
||||
@@ -1540,6 +1542,7 @@ async function showBatchQueueDetail(queueId) {
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取队列详情失败:', error);
|
||||
closeBatchQueueDetailModal();
|
||||
alert(_t('tasks.getQueueDetailFailed') + ': ' + error.message);
|
||||
}
|
||||
}
|
||||
@@ -1708,10 +1711,7 @@ async function deleteBatchQueueFromList(queueId) {
|
||||
|
||||
// 关闭批量任务队列详情模态框
|
||||
function closeBatchQueueDetailModal() {
|
||||
const modal = document.getElementById('batch-queue-detail-modal');
|
||||
if (modal) {
|
||||
modal.style.display = 'none';
|
||||
}
|
||||
closeAppModal('batch-queue-detail-modal');
|
||||
batchQueuesState.currentQueueId = null;
|
||||
stopBatchQueueRefresh();
|
||||
}
|
||||
@@ -1730,7 +1730,7 @@ function startBatchQueueRefresh(queueId) {
|
||||
content.querySelector('.bq-inline-edit-controls') ||
|
||||
content.querySelector('.batch-task-inline-edit')
|
||||
);
|
||||
if ((addModal && addModal.style.display === 'block') || hasInlineEdit) {
|
||||
if ((addModal && isAppModalOpen('add-batch-task-modal')) || hasInlineEdit) {
|
||||
return;
|
||||
}
|
||||
if (batchQueuesState._bqDetailRefreshing) {
|
||||
@@ -1891,12 +1891,7 @@ function showAddBatchTaskModal() {
|
||||
}
|
||||
|
||||
messageInput.value = '';
|
||||
modal.style.display = 'block';
|
||||
|
||||
// 聚焦到输入框
|
||||
setTimeout(() => {
|
||||
messageInput.focus();
|
||||
}, 100);
|
||||
openAppModal('add-batch-task-modal', { focusEl: messageInput });
|
||||
|
||||
// 清理旧的事件监听器
|
||||
if (showAddBatchTaskModal._escHandler) {
|
||||
@@ -1940,9 +1935,7 @@ function closeAddBatchTaskModal() {
|
||||
}
|
||||
const modal = document.getElementById('add-batch-task-modal');
|
||||
const messageInput = document.getElementById('add-task-message');
|
||||
if (modal) {
|
||||
modal.style.display = 'none';
|
||||
}
|
||||
closeAppModal('add-batch-task-modal');
|
||||
if (messageInput) {
|
||||
messageInput.value = '';
|
||||
}
|
||||
@@ -2462,7 +2455,7 @@ document.addEventListener('languagechange', function () {
|
||||
const detailModal = document.getElementById('batch-queue-detail-modal');
|
||||
if (
|
||||
detailModal &&
|
||||
detailModal.style.display === 'block' &&
|
||||
isAppModalOpen('batch-queue-detail-modal') &&
|
||||
batchQueuesState.currentQueueId
|
||||
) {
|
||||
showBatchQueueDetail(batchQueuesState.currentQueueId);
|
||||
|
||||
@@ -1090,37 +1090,36 @@ async function showAddVulnerabilityModal() {
|
||||
document.getElementById('vulnerability-impact').value = '';
|
||||
document.getElementById('vulnerability-recommendation').value = '';
|
||||
|
||||
document.getElementById('vulnerability-modal').style.display = 'block';
|
||||
openAppModal('vulnerability-modal');
|
||||
}
|
||||
|
||||
// 编辑漏洞
|
||||
async function editVulnerability(id) {
|
||||
try {
|
||||
const response = await apiFetch(`/api/vulnerabilities/${id}`);
|
||||
if (!response.ok) throw new Error(vulnT('vulnerabilityPage.fetchFailed'));
|
||||
|
||||
const vuln = await response.json();
|
||||
currentVulnerabilityId = id;
|
||||
document.getElementById('vulnerability-modal-title').textContent = vulnT('vulnerability.editVuln');
|
||||
|
||||
// 填充表单
|
||||
document.getElementById('vulnerability-conversation-id').value = vuln.conversation_id || '';
|
||||
document.getElementById('vulnerability-conversation-tag').value = vuln.conversation_tag || '';
|
||||
document.getElementById('vulnerability-task-tag').value = vuln.task_tag || '';
|
||||
document.getElementById('vulnerability-title').value = vuln.title || '';
|
||||
document.getElementById('vulnerability-description').value = vuln.description || '';
|
||||
document.getElementById('vulnerability-severity').value = vuln.severity || '';
|
||||
document.getElementById('vulnerability-status').value = vuln.status || 'open';
|
||||
document.getElementById('vulnerability-type').value = vuln.type || '';
|
||||
document.getElementById('vulnerability-target').value = vuln.target || '';
|
||||
document.getElementById('vulnerability-proof').value = vuln.proof || '';
|
||||
document.getElementById('vulnerability-impact').value = vuln.impact || '';
|
||||
document.getElementById('vulnerability-recommendation').value = vuln.recommendation || '';
|
||||
|
||||
await populateVulnerabilityModalProjectSelect(vuln.project_id || '');
|
||||
|
||||
document.getElementById('vulnerability-modal').style.display = 'block';
|
||||
openAppModal('vulnerability-modal', { focus: false });
|
||||
const response = await apiFetch(`/api/vulnerabilities/${id}`);
|
||||
if (!response.ok) throw new Error(vulnT('vulnerabilityPage.fetchFailed'));
|
||||
const vuln = await response.json();
|
||||
deferModalContent(async () => {
|
||||
document.getElementById('vulnerability-conversation-id').value = vuln.conversation_id || '';
|
||||
document.getElementById('vulnerability-conversation-tag').value = vuln.conversation_tag || '';
|
||||
document.getElementById('vulnerability-task-tag').value = vuln.task_tag || '';
|
||||
document.getElementById('vulnerability-title').value = vuln.title || '';
|
||||
document.getElementById('vulnerability-description').value = vuln.description || '';
|
||||
document.getElementById('vulnerability-severity').value = vuln.severity || '';
|
||||
document.getElementById('vulnerability-status').value = vuln.status || 'open';
|
||||
document.getElementById('vulnerability-type').value = vuln.type || '';
|
||||
document.getElementById('vulnerability-target').value = vuln.target || '';
|
||||
document.getElementById('vulnerability-proof').value = vuln.proof || '';
|
||||
document.getElementById('vulnerability-impact').value = vuln.impact || '';
|
||||
document.getElementById('vulnerability-recommendation').value = vuln.recommendation || '';
|
||||
await populateVulnerabilityModalProjectSelect(vuln.project_id || '');
|
||||
document.getElementById('vulnerability-title')?.focus();
|
||||
});
|
||||
} catch (error) {
|
||||
closeVulnerabilityModal();
|
||||
console.error('加载漏洞失败:', error);
|
||||
alert(vulnT('vulnerability.loadFailed') + ': ' + error.message);
|
||||
}
|
||||
@@ -1233,7 +1232,7 @@ async function deleteVulnerability(id) {
|
||||
|
||||
// 关闭漏洞模态框
|
||||
function closeVulnerabilityModal() {
|
||||
document.getElementById('vulnerability-modal').style.display = 'none';
|
||||
closeAppModal('vulnerability-modal');
|
||||
currentVulnerabilityId = null;
|
||||
}
|
||||
|
||||
@@ -1749,7 +1748,7 @@ async function refreshVulnerabilityProjectFilter() {
|
||||
sel.innerHTML = html;
|
||||
if (cur) sel.value = cur;
|
||||
const modalSel = document.getElementById('vulnerability-project-id');
|
||||
if (modalSel && document.getElementById('vulnerability-modal')?.style.display === 'block') {
|
||||
if (modalSel && isAppModalOpen('vulnerability-modal')) {
|
||||
const modalCur = modalSel.value || '';
|
||||
modalSel.innerHTML = buildVulnerabilityProjectOptionsHtml(modalCur);
|
||||
modalSel.value = modalCur;
|
||||
|
||||
+27
-22
@@ -2301,10 +2301,14 @@ function selectWebshell(id, stateReady) {
|
||||
|
||||
function setDbProfileModalVisible(visible, mode) {
|
||||
if (!dbProfileModalEl) return;
|
||||
dbProfileModalEl.style.display = visible ? 'block' : 'none';
|
||||
if (dbProfileModalTitleEl) {
|
||||
if (mode === 'add') dbProfileModalTitleEl.textContent = wsT('webshell.dbAddProfile') || '新增连接';
|
||||
else dbProfileModalTitleEl.textContent = wsT('webshell.editConnectionTitle') || '编辑连接';
|
||||
if (visible) {
|
||||
if (dbProfileModalTitleEl) {
|
||||
if (mode === 'add') dbProfileModalTitleEl.textContent = wsT('webshell.dbAddProfile') || '新增连接';
|
||||
else dbProfileModalTitleEl.textContent = wsT('webshell.editConnectionTitle') || '编辑连接';
|
||||
}
|
||||
openAppModal(dbProfileModalEl);
|
||||
} else {
|
||||
closeAppModal(dbProfileModalEl);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4369,37 +4373,38 @@ function showAddWebshellModal() {
|
||||
var titleEl = document.getElementById('webshell-modal-title');
|
||||
if (titleEl) titleEl.textContent = wsT('webshell.addConnection');
|
||||
var modal = document.getElementById('webshell-modal');
|
||||
if (modal) modal.style.display = 'block';
|
||||
if (modal) openAppModal(modal);
|
||||
}
|
||||
|
||||
// 打开编辑连接弹窗(预填当前连接信息)
|
||||
function showEditWebshellModal(connId) {
|
||||
var conn = webshellConnections.find(function (c) { return c.id === connId; });
|
||||
if (!conn) return;
|
||||
var editIdEl = document.getElementById('webshell-edit-id');
|
||||
if (editIdEl) editIdEl.value = conn.id;
|
||||
document.getElementById('webshell-url').value = conn.url || '';
|
||||
document.getElementById('webshell-password').value = conn.password || '';
|
||||
document.getElementById('webshell-type').value = conn.type || 'php';
|
||||
document.getElementById('webshell-method').value = (conn.method || 'post').toLowerCase();
|
||||
document.getElementById('webshell-cmd-param').value = conn.cmdParam || '';
|
||||
var osEditEl = document.getElementById('webshell-os');
|
||||
if (osEditEl) osEditEl.value = normalizeWebshellOS(conn.os);
|
||||
var encEditEl = document.getElementById('webshell-encoding');
|
||||
if (encEditEl) encEditEl.value = normalizeWebshellEncoding(conn.encoding);
|
||||
document.getElementById('webshell-remark').value = conn.remark || '';
|
||||
var titleEl = document.getElementById('webshell-modal-title');
|
||||
if (titleEl) titleEl.textContent = wsT('webshell.editConnectionTitle');
|
||||
var modal = document.getElementById('webshell-modal');
|
||||
if (modal) modal.style.display = 'block';
|
||||
openAppModal('webshell-modal', { focus: false });
|
||||
deferModalContent(function () {
|
||||
var editIdEl = document.getElementById('webshell-edit-id');
|
||||
if (editIdEl) editIdEl.value = conn.id;
|
||||
document.getElementById('webshell-url').value = conn.url || '';
|
||||
document.getElementById('webshell-password').value = conn.password || '';
|
||||
document.getElementById('webshell-type').value = conn.type || 'php';
|
||||
document.getElementById('webshell-method').value = (conn.method || 'post').toLowerCase();
|
||||
document.getElementById('webshell-cmd-param').value = conn.cmdParam || '';
|
||||
var osEditEl = document.getElementById('webshell-os');
|
||||
if (osEditEl) osEditEl.value = normalizeWebshellOS(conn.os);
|
||||
var encEditEl = document.getElementById('webshell-encoding');
|
||||
if (encEditEl) encEditEl.value = normalizeWebshellEncoding(conn.encoding);
|
||||
document.getElementById('webshell-remark').value = conn.remark || '';
|
||||
document.getElementById('webshell-url')?.focus();
|
||||
});
|
||||
}
|
||||
|
||||
// 关闭弹窗
|
||||
function closeWebshellModal() {
|
||||
var editIdEl = document.getElementById('webshell-edit-id');
|
||||
if (editIdEl) editIdEl.value = '';
|
||||
var modal = document.getElementById('webshell-modal');
|
||||
if (modal) modal.style.display = 'none';
|
||||
closeAppModal('webshell-modal');
|
||||
}
|
||||
|
||||
// 语言切换时刷新 WebShell 页面内所有由 JS 生成的文案(不重建终端)
|
||||
@@ -4571,7 +4576,7 @@ function refreshWebshellUIOnLanguageChange() {
|
||||
}
|
||||
|
||||
var modal = document.getElementById('webshell-modal');
|
||||
if (modal && modal.style.display === 'block') {
|
||||
if (modal && isAppModalOpen('webshell-modal')) {
|
||||
var titleEl = document.getElementById('webshell-modal-title');
|
||||
var editIdEl = document.getElementById('webshell-edit-id');
|
||||
if (titleEl) {
|
||||
|
||||
@@ -4172,7 +4172,7 @@
|
||||
<div class="projects-modal-body">
|
||||
<div class="projects-form-field">
|
||||
<label for="project-modal-name" data-i18n="projects.projectName">项目名称 <span class="required">*</span></label>
|
||||
<input type="text" id="project-modal-name" class="form-input" placeholder="例如:某客户 Web 渗透" autocomplete="off" data-i18n="projects.projectNamePlaceholder" data-i18n-attr="placeholder">
|
||||
<input type="text" id="project-modal-name" class="form-input" maxlength="200" placeholder="例如:某客户 Web 渗透" autocomplete="off" data-i18n="projects.projectNamePlaceholder" data-i18n-attr="placeholder">
|
||||
</div>
|
||||
<div class="projects-form-field">
|
||||
<label for="project-modal-description" data-i18n="projects.projectDescription">项目描述</label>
|
||||
@@ -4290,6 +4290,7 @@
|
||||
<script src="/static/js/i18n.js"></script>
|
||||
<script src="/static/js/builtin-tools.js"></script>
|
||||
<script src="/static/js/auth.js"></script>
|
||||
<script src="/static/js/modal.js"></script>
|
||||
<script src="/static/js/notifications.js"></script>
|
||||
<script src="/static/js/info-collect.js"></script>
|
||||
<script src="/static/js/router.js"></script>
|
||||
|
||||
Reference in New Issue
Block a user