mirror of
https://github.com/Ed1s0nZ/CyberStrikeAI.git
synced 2026-06-26 16:00:06 +02:00
Add files via upload
This commit is contained in:
+243
-6
@@ -1615,9 +1615,34 @@ header {
|
||||
|
||||
.conversation-search-box {
|
||||
position: relative;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.conversation-sidebar .sidebar-content {
|
||||
padding: 10px 16px 16px;
|
||||
}
|
||||
|
||||
.conversation-sidebar .conversation-search-box {
|
||||
margin-top: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.conversation-sidebar .conversation-project-filter {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.conversation-sidebar .conversation-groups-section {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.conversation-sidebar .recent-conversations-section {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.conversation-sidebar .section-header {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.conversation-search-box input {
|
||||
width: 100%;
|
||||
padding: 8px 32px 8px 12px;
|
||||
@@ -1668,6 +1693,170 @@ header {
|
||||
height: 14px;
|
||||
}
|
||||
|
||||
.conversation-project-filter {
|
||||
margin-bottom: 12px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.conversation-project-filter-label {
|
||||
display: block;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 500;
|
||||
color: var(--text-muted);
|
||||
margin-bottom: 4px;
|
||||
padding: 0 2px;
|
||||
}
|
||||
|
||||
.conversation-project-filter-native {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
padding: 0;
|
||||
margin: -1px;
|
||||
overflow: hidden;
|
||||
clip: rect(0, 0, 0, 0);
|
||||
white-space: nowrap;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.conversation-project-filter-ui {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.conversation-project-filter-trigger {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 8px;
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
padding: 8px 10px;
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 6px;
|
||||
background: var(--bg-primary);
|
||||
color: var(--text-primary);
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.25;
|
||||
cursor: pointer;
|
||||
font-family: inherit;
|
||||
transition: border-color 0.2s ease, box-shadow 0.2s ease;
|
||||
}
|
||||
|
||||
.conversation-project-filter-trigger:hover:not(:disabled) {
|
||||
border-color: var(--accent-color);
|
||||
}
|
||||
|
||||
.conversation-project-filter-ui.open .conversation-project-filter-trigger {
|
||||
outline: none;
|
||||
border-color: var(--accent-color);
|
||||
box-shadow: 0 0 0 3px rgba(0, 102, 255, 0.1);
|
||||
}
|
||||
|
||||
.conversation-project-filter-ui.open {
|
||||
z-index: 120;
|
||||
}
|
||||
|
||||
.conversation-project-filter-value {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.conversation-project-filter-caret {
|
||||
flex-shrink: 0;
|
||||
color: var(--text-secondary);
|
||||
transition: transform 0.15s ease;
|
||||
}
|
||||
|
||||
.conversation-project-filter-ui.open .conversation-project-filter-caret {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
.conversation-project-filter-dropdown {
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: calc(100% + 4px);
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 200;
|
||||
max-height: 280px;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
padding: 4px;
|
||||
background: var(--bg-primary);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 8px;
|
||||
box-shadow: var(--shadow-lg);
|
||||
}
|
||||
|
||||
.conversation-project-filter-ui.open .conversation-project-filter-dropdown {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.conversation-project-filter-option {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
padding: 8px 10px;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
background: transparent;
|
||||
color: var(--text-primary);
|
||||
font-size: 0.8125rem;
|
||||
font-family: inherit;
|
||||
cursor: pointer;
|
||||
text-align: left;
|
||||
transition: background 0.12s ease, color 0.12s ease;
|
||||
}
|
||||
|
||||
.conversation-project-filter-option:hover {
|
||||
background: var(--bg-secondary);
|
||||
}
|
||||
|
||||
.conversation-project-filter-option.is-selected {
|
||||
background: rgba(0, 102, 255, 0.08);
|
||||
color: var(--accent-color);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.conversation-project-filter-check {
|
||||
width: 14px;
|
||||
flex-shrink: 0;
|
||||
opacity: 0;
|
||||
font-size: 0.75rem;
|
||||
line-height: 1;
|
||||
color: var(--accent-color);
|
||||
}
|
||||
|
||||
.conversation-project-filter-option.is-selected .conversation-project-filter-check {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.conversation-project-filter-option-label {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.conversation-item-project-badge {
|
||||
font-size: 0.6875rem;
|
||||
color: var(--text-muted);
|
||||
margin-top: 2px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.conversations-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -11196,6 +11385,7 @@ tr.mcp-stats-tool-row[data-tool-name]:focus-visible {
|
||||
.conversation-groups-section,
|
||||
.recent-conversations-section {
|
||||
margin-bottom: 24px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.conversation-groups-section:last-child,
|
||||
@@ -11209,6 +11399,8 @@ tr.mcp-stats-tool-row[data-tool-name]:focus-visible {
|
||||
justify-content: space-between;
|
||||
margin-bottom: 12px;
|
||||
padding: 0 8px;
|
||||
min-width: 0;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.section-header-actions {
|
||||
@@ -11337,6 +11529,21 @@ tr.mcp-stats-tool-row[data-tool-name]:focus-visible {
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.recent-conversations-section .section-title {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.recent-conversations-section .section-title.section-title--filtered {
|
||||
text-transform: none;
|
||||
letter-spacing: normal;
|
||||
font-size: 0.875rem;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.add-group-btn,
|
||||
.batch-manage-btn {
|
||||
width: 24px;
|
||||
@@ -11729,7 +11936,7 @@ tr.mcp-stats-tool-row[data-tool-name]:focus-visible {
|
||||
|
||||
/* 批量管理模态框 */
|
||||
.batch-manage-modal-content {
|
||||
max-width: 800px;
|
||||
max-width: 920px;
|
||||
width: 90vw;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -11739,7 +11946,23 @@ tr.mcp-stats-tool-row[data-tool-name]:focus-visible {
|
||||
.batch-manage-header-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
gap: 10px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.batch-manage-header-actions .conversation-project-filter-ui {
|
||||
width: 148px;
|
||||
min-width: 108px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.batch-manage-header-actions .conversation-project-filter-trigger {
|
||||
font-size: 0.8125rem;
|
||||
padding: 8px 10px;
|
||||
}
|
||||
|
||||
.batch-manage-modal-content .conversation-project-filter-ui.open {
|
||||
z-index: 400;
|
||||
}
|
||||
|
||||
.batch-search-box {
|
||||
@@ -11783,8 +12006,8 @@ tr.mcp-stats-tool-row[data-tool-name]:focus-visible {
|
||||
|
||||
.batch-table-header {
|
||||
display: grid;
|
||||
grid-template-columns: 40px 1fr 180px 80px;
|
||||
gap: 16px;
|
||||
grid-template-columns: 40px minmax(0, 1.2fr) minmax(0, 0.9fr) 160px 72px;
|
||||
gap: 12px;
|
||||
padding: 12px 16px;
|
||||
background: var(--bg-secondary);
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
@@ -11802,8 +12025,8 @@ tr.mcp-stats-tool-row[data-tool-name]:focus-visible {
|
||||
|
||||
.batch-conversation-row {
|
||||
display: grid;
|
||||
grid-template-columns: 40px 1fr 180px 80px;
|
||||
gap: 16px;
|
||||
grid-template-columns: 40px minmax(0, 1.2fr) minmax(0, 0.9fr) 160px 72px;
|
||||
gap: 12px;
|
||||
padding: 12px 16px;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
align-items: center;
|
||||
@@ -11830,6 +12053,20 @@ tr.mcp-stats-tool-row[data-tool-name]:focus-visible {
|
||||
/* 完全依赖JavaScript截断,禁用CSS的ellipsis以避免在UTF-8多字节字符中间截断 */
|
||||
}
|
||||
|
||||
.batch-table-col-project {
|
||||
font-size: 0.8125rem;
|
||||
color: var(--text-muted);
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.batch-table-col-project.is-unbound {
|
||||
color: var(--text-muted);
|
||||
font-style: italic;
|
||||
opacity: 0.85;
|
||||
}
|
||||
|
||||
.batch-table-col-time {
|
||||
font-size: 0.875rem;
|
||||
color: var(--text-muted);
|
||||
|
||||
@@ -499,6 +499,13 @@
|
||||
"conversationGroups": "Conversation groups",
|
||||
"addGroup": "New group",
|
||||
"recentConversations": "Recent conversations",
|
||||
"filterByProject": "Filter by project",
|
||||
"filterAllProjects": "All projects",
|
||||
"filterUnboundProjects": "Unbound",
|
||||
"projectConversationsTitle": "{{name}} · Conversations",
|
||||
"unboundConversationsTitle": "Unbound conversations",
|
||||
"noProjectConversations": "No conversations in this project",
|
||||
"noUnboundConversations": "No unbound conversations",
|
||||
"sortConversations": "Sort",
|
||||
"sortByCreatedAt": "Created time",
|
||||
"sortByUpdatedAt": "Updated time",
|
||||
@@ -2527,6 +2534,9 @@
|
||||
"title": "Manage conversations · {{count}} total",
|
||||
"searchPlaceholder": "Search history",
|
||||
"conversationName": "Conversation name",
|
||||
"project": "Project",
|
||||
"noProject": "No project",
|
||||
"filterByProject": "Filter by project",
|
||||
"lastTime": "Last activity",
|
||||
"action": "Action",
|
||||
"selectAll": "Select all",
|
||||
|
||||
@@ -487,6 +487,13 @@
|
||||
"conversationGroups": "对话分组",
|
||||
"addGroup": "新建分组",
|
||||
"recentConversations": "最近对话",
|
||||
"filterByProject": "按项目筛选",
|
||||
"filterAllProjects": "全部项目",
|
||||
"filterUnboundProjects": "未绑定项目",
|
||||
"projectConversationsTitle": "{{name}} · 对话",
|
||||
"unboundConversationsTitle": "未绑定项目",
|
||||
"noProjectConversations": "该项目暂无对话",
|
||||
"noUnboundConversations": "暂无未绑定项目的对话",
|
||||
"sortConversations": "排序",
|
||||
"sortByCreatedAt": "创建时间",
|
||||
"sortByUpdatedAt": "更新时间",
|
||||
@@ -2515,6 +2522,9 @@
|
||||
"title": "管理对话记录·共{{count}}条",
|
||||
"searchPlaceholder": "搜索历史记录",
|
||||
"conversationName": "对话名称",
|
||||
"project": "项目",
|
||||
"noProject": "无项目",
|
||||
"filterByProject": "按项目筛选",
|
||||
"lastTime": "最近一次对话时间",
|
||||
"action": "操作",
|
||||
"selectAll": "全选",
|
||||
|
||||
+425
-34
@@ -3322,6 +3322,18 @@ function createConversationListItem(conversation) {
|
||||
title.title = titleText; // 设置完整标题以便悬停查看
|
||||
contentWrapper.appendChild(title);
|
||||
|
||||
if (!getConversationProjectFilter()) {
|
||||
const pid = conversation.projectId || conversation.project_id || '';
|
||||
const projectName = pid && window.projectNameById ? window.projectNameById[pid] : '';
|
||||
if (projectName) {
|
||||
const badge = document.createElement('div');
|
||||
badge.className = 'conversation-item-project-badge';
|
||||
badge.textContent = projectName;
|
||||
badge.title = projectName;
|
||||
contentWrapper.appendChild(badge);
|
||||
}
|
||||
}
|
||||
|
||||
const time = document.createElement('div');
|
||||
time.className = 'conversation-time';
|
||||
time.textContent = conversation._timeText || formatConversationTimestamp(conversation._time || new Date());
|
||||
@@ -3867,14 +3879,7 @@ async function deleteConversation(conversationId, skipConfirm = false) {
|
||||
const batchModal = document.getElementById('batch-manage-modal');
|
||||
if (batchModal && isAppModalOpen('batch-manage-modal')) {
|
||||
allConversationsForBatch = allConversationsForBatch.filter(c => c.id !== conversationId);
|
||||
updateBatchManageTitle(allConversationsForBatch.length);
|
||||
const searchInput = document.getElementById('batch-search-input');
|
||||
const query = searchInput ? searchInput.value : '';
|
||||
if (query && query.trim()) {
|
||||
filterBatchConversations(query);
|
||||
} else {
|
||||
renderBatchConversations();
|
||||
}
|
||||
applyBatchConversationFilters();
|
||||
}
|
||||
|
||||
// 通知其他模块(如 WebShell AI 助手)同步删除,保持列表一致
|
||||
@@ -6075,6 +6080,266 @@ let pendingGroupMappings = {}; // 待保留的分组映射(用于处理后端A
|
||||
let conversationsListLoadSeq = 0; // 对话列表加载序号,避免并发请求导致重复渲染
|
||||
const CONVERSATIONS_PAGE_SIZE_KEY = 'cyberstrike.conversations_page_size';
|
||||
const CONVERSATIONS_SORT_KEY = 'cyberstrike.conversations_sort_by';
|
||||
const CONVERSATIONS_PROJECT_FILTER_KEY = 'cyberstrike.conversations_project_filter';
|
||||
const CONVERSATION_PROJECT_FILTER_NONE = '__none__';
|
||||
const CONVERSATION_PROJECT_FILTER_SELECT_ID = 'conversation-project-filter';
|
||||
const CONVERSATION_PROJECT_FILTER_CARET = '<svg class="conversation-project-filter-caret" width="14" height="14" viewBox="0 0 24 24" fill="none" aria-hidden="true"><path d="M6 9l6 6 6-6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>';
|
||||
const BATCH_PROJECT_FILTER_SELECT_ID = 'batch-project-filter';
|
||||
const projectFilterCustomSelectRegistry = {};
|
||||
let projectFilterCustomSelectDocBound = false;
|
||||
|
||||
function closeProjectFilterCustomSelect(selectId) {
|
||||
const reg = projectFilterCustomSelectRegistry[selectId];
|
||||
if (!reg || !reg.wrapper) return;
|
||||
reg.wrapper.classList.remove('open');
|
||||
if (reg.trigger) reg.trigger.setAttribute('aria-expanded', 'false');
|
||||
}
|
||||
|
||||
function closeAllProjectFilterCustomSelects() {
|
||||
Object.keys(projectFilterCustomSelectRegistry).forEach(closeProjectFilterCustomSelect);
|
||||
}
|
||||
|
||||
function syncProjectFilterCustomSelect(selectId) {
|
||||
const reg = projectFilterCustomSelectRegistry[selectId];
|
||||
if (!reg) return;
|
||||
const { select, dropdown, trigger } = reg;
|
||||
const valueSpan = trigger.querySelector('.conversation-project-filter-value');
|
||||
dropdown.innerHTML = '';
|
||||
Array.prototype.forEach.call(select.options, (opt) => {
|
||||
const item = document.createElement('button');
|
||||
item.type = 'button';
|
||||
item.className = 'conversation-project-filter-option';
|
||||
item.setAttribute('role', 'option');
|
||||
item.setAttribute('data-value', opt.value);
|
||||
const labelText = opt.textContent || '';
|
||||
item.title = labelText;
|
||||
if (opt.value === select.value) {
|
||||
item.classList.add('is-selected');
|
||||
item.setAttribute('aria-selected', 'true');
|
||||
} else {
|
||||
item.setAttribute('aria-selected', 'false');
|
||||
}
|
||||
const check = document.createElement('span');
|
||||
check.className = 'conversation-project-filter-check';
|
||||
check.setAttribute('aria-hidden', 'true');
|
||||
check.textContent = '✓';
|
||||
const label = document.createElement('span');
|
||||
label.className = 'conversation-project-filter-option-label';
|
||||
label.textContent = labelText;
|
||||
label.title = labelText;
|
||||
item.appendChild(check);
|
||||
item.appendChild(label);
|
||||
dropdown.appendChild(item);
|
||||
});
|
||||
const selectedOpt = select.options[select.selectedIndex];
|
||||
const selectedText = selectedOpt ? (selectedOpt.textContent || '') : '';
|
||||
if (valueSpan) {
|
||||
valueSpan.textContent = selectedText;
|
||||
valueSpan.title = selectedText;
|
||||
}
|
||||
}
|
||||
|
||||
function initProjectFilterCustomSelect(selectId) {
|
||||
const select = document.getElementById(selectId);
|
||||
if (!select) return;
|
||||
if (select.dataset.projectCustomSelect === '1') {
|
||||
syncProjectFilterCustomSelect(selectId);
|
||||
return;
|
||||
}
|
||||
select.dataset.projectCustomSelect = '1';
|
||||
select.classList.add('conversation-project-filter-native');
|
||||
select.tabIndex = -1;
|
||||
select.setAttribute('aria-hidden', 'true');
|
||||
|
||||
const wrapper = document.createElement('div');
|
||||
wrapper.className = 'conversation-project-filter-ui';
|
||||
|
||||
const trigger = document.createElement('button');
|
||||
trigger.type = 'button';
|
||||
trigger.className = 'conversation-project-filter-trigger';
|
||||
trigger.setAttribute('aria-haspopup', 'listbox');
|
||||
trigger.setAttribute('aria-expanded', 'false');
|
||||
const valueSpan = document.createElement('span');
|
||||
valueSpan.className = 'conversation-project-filter-value';
|
||||
trigger.appendChild(valueSpan);
|
||||
trigger.insertAdjacentHTML('beforeend', CONVERSATION_PROJECT_FILTER_CARET);
|
||||
|
||||
const dropdown = document.createElement('div');
|
||||
dropdown.className = 'conversation-project-filter-dropdown';
|
||||
dropdown.setAttribute('role', 'listbox');
|
||||
|
||||
const parent = select.parentNode;
|
||||
parent.insertBefore(wrapper, select);
|
||||
wrapper.appendChild(trigger);
|
||||
wrapper.appendChild(dropdown);
|
||||
wrapper.appendChild(select);
|
||||
|
||||
projectFilterCustomSelectRegistry[selectId] = { wrapper, trigger, dropdown, select };
|
||||
|
||||
trigger.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
const open = wrapper.classList.contains('open');
|
||||
closeAllProjectFilterCustomSelects();
|
||||
if (!open) {
|
||||
wrapper.classList.add('open');
|
||||
trigger.setAttribute('aria-expanded', 'true');
|
||||
}
|
||||
});
|
||||
|
||||
dropdown.addEventListener('click', (e) => {
|
||||
const opt = e.target.closest('.conversation-project-filter-option');
|
||||
if (!opt) return;
|
||||
e.stopPropagation();
|
||||
const val = opt.getAttribute('data-value');
|
||||
if (val === null) return;
|
||||
if (select.value !== val) {
|
||||
select.value = val;
|
||||
select.dispatchEvent(new Event('change', { bubbles: true }));
|
||||
}
|
||||
closeProjectFilterCustomSelect(selectId);
|
||||
syncProjectFilterCustomSelect(selectId);
|
||||
});
|
||||
|
||||
if (!projectFilterCustomSelectDocBound) {
|
||||
projectFilterCustomSelectDocBound = true;
|
||||
document.addEventListener('click', closeAllProjectFilterCustomSelects);
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Escape') closeAllProjectFilterCustomSelects();
|
||||
});
|
||||
}
|
||||
syncProjectFilterCustomSelect(selectId);
|
||||
}
|
||||
|
||||
function syncConversationProjectCustomSelect() {
|
||||
syncProjectFilterCustomSelect(CONVERSATION_PROJECT_FILTER_SELECT_ID);
|
||||
}
|
||||
|
||||
function initConversationProjectCustomSelect() {
|
||||
initProjectFilterCustomSelect(CONVERSATION_PROJECT_FILTER_SELECT_ID);
|
||||
}
|
||||
|
||||
function getConversationProjectFilter() {
|
||||
try {
|
||||
return localStorage.getItem(CONVERSATIONS_PROJECT_FILTER_KEY) || '';
|
||||
} catch (e) {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
function setConversationProjectFilter(projectId) {
|
||||
const value = (projectId || '').trim();
|
||||
try {
|
||||
if (value) localStorage.setItem(CONVERSATIONS_PROJECT_FILTER_KEY, value);
|
||||
else localStorage.removeItem(CONVERSATIONS_PROJECT_FILTER_KEY);
|
||||
} catch (e) { /* ignore */ }
|
||||
const sel = document.getElementById('conversation-project-filter');
|
||||
if (sel && sel.value !== value) sel.value = value;
|
||||
syncConversationProjectCustomSelect();
|
||||
updateConversationSidebarFilterUI();
|
||||
}
|
||||
|
||||
function isValidConversationProjectFilter(projectId) {
|
||||
if (!projectId) return true;
|
||||
if (projectId === CONVERSATION_PROJECT_FILTER_NONE) return true;
|
||||
const map = window.projectNameById;
|
||||
if (!map || typeof map !== 'object') return true;
|
||||
return Object.prototype.hasOwnProperty.call(map, projectId);
|
||||
}
|
||||
|
||||
async function refreshConversationProjectFilter() {
|
||||
const sel = document.getElementById('conversation-project-filter');
|
||||
if (!sel) return;
|
||||
const saved = getConversationProjectFilter();
|
||||
let projects = [];
|
||||
if (typeof window.ensureProjectsLoaded === 'function') {
|
||||
try {
|
||||
const list = await window.ensureProjectsLoaded();
|
||||
projects = (list || []).filter((p) => p && p.id && p.status !== 'archived');
|
||||
} catch (e) { /* ignore */ }
|
||||
}
|
||||
if (!projects.length) {
|
||||
try {
|
||||
const res = await apiFetch('/api/projects?status=active&limit=200');
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
const items = data.projects || data.items || (Array.isArray(data) ? data : []);
|
||||
projects = items.filter((p) => p && p.id);
|
||||
if (typeof window.rebuildProjectNameMap === 'function') {
|
||||
window.rebuildProjectNameMap(items);
|
||||
}
|
||||
}
|
||||
} catch (e) { /* ignore */ }
|
||||
}
|
||||
const tFn = typeof window.t === 'function' ? window.t.bind(window) : null;
|
||||
const allLabel = tFn ? tFn('chat.filterAllProjects') : '全部项目';
|
||||
const unboundLabel = tFn ? tFn('chat.filterUnboundProjects') : '未绑定项目';
|
||||
sel.innerHTML = '';
|
||||
const allOpt = document.createElement('option');
|
||||
allOpt.value = '';
|
||||
allOpt.textContent = allLabel;
|
||||
allOpt.setAttribute('data-i18n', 'chat.filterAllProjects');
|
||||
sel.appendChild(allOpt);
|
||||
const unboundOpt = document.createElement('option');
|
||||
unboundOpt.value = CONVERSATION_PROJECT_FILTER_NONE;
|
||||
unboundOpt.textContent = unboundLabel;
|
||||
unboundOpt.setAttribute('data-i18n', 'chat.filterUnboundProjects');
|
||||
sel.appendChild(unboundOpt);
|
||||
projects
|
||||
.slice()
|
||||
.sort((a, b) => (a.name || a.id || '').localeCompare(b.name || b.id || '', undefined, { sensitivity: 'base' }))
|
||||
.forEach((p) => {
|
||||
const opt = document.createElement('option');
|
||||
opt.value = p.id;
|
||||
opt.textContent = p.name || p.id;
|
||||
sel.appendChild(opt);
|
||||
});
|
||||
const normalized = isValidConversationProjectFilter(saved) ? saved : '';
|
||||
if (normalized !== saved) setConversationProjectFilter(normalized);
|
||||
sel.value = normalized;
|
||||
syncConversationProjectCustomSelect();
|
||||
updateConversationSidebarFilterUI();
|
||||
}
|
||||
|
||||
function onConversationProjectFilterChange(projectId) {
|
||||
setConversationProjectFilter(projectId || '');
|
||||
conversationsPagination.page = 1;
|
||||
loadConversationsWithGroups(conversationsSearchQuery);
|
||||
}
|
||||
|
||||
function updateConversationSidebarFilterUI() {
|
||||
const groupsSection = document.querySelector('.conversation-groups-section');
|
||||
const titleEl = document.querySelector('.recent-conversations-section .section-title');
|
||||
const filter = getConversationProjectFilter();
|
||||
const hasSearch = !!(conversationsSearchQuery && conversationsSearchQuery.trim());
|
||||
if (groupsSection) {
|
||||
groupsSection.hidden = !!filter || hasSearch;
|
||||
}
|
||||
if (!titleEl) return;
|
||||
const tFn = typeof window.t === 'function' ? window.t.bind(window) : null;
|
||||
if (filter && filter !== CONVERSATION_PROJECT_FILTER_NONE) {
|
||||
const name = (window.projectNameById && window.projectNameById[filter]) || filter;
|
||||
const fullTitle = tFn ? tFn('chat.projectConversationsTitle', { name }) : `${name} · 对话`;
|
||||
titleEl.textContent = fullTitle;
|
||||
titleEl.title = fullTitle;
|
||||
titleEl.classList.add('section-title--filtered');
|
||||
titleEl.removeAttribute('data-i18n');
|
||||
} else if (filter === CONVERSATION_PROJECT_FILTER_NONE) {
|
||||
const fullTitle = tFn ? tFn('chat.unboundConversationsTitle') : '未绑定项目';
|
||||
titleEl.textContent = fullTitle;
|
||||
titleEl.title = fullTitle;
|
||||
titleEl.classList.add('section-title--filtered');
|
||||
titleEl.setAttribute('data-i18n', 'chat.unboundConversationsTitle');
|
||||
} else {
|
||||
titleEl.classList.remove('section-title--filtered');
|
||||
titleEl.removeAttribute('title');
|
||||
titleEl.setAttribute('data-i18n', 'chat.recentConversations');
|
||||
if (tFn) titleEl.textContent = tFn('chat.recentConversations');
|
||||
}
|
||||
}
|
||||
|
||||
window.onConversationProjectBindingChanged = function onConversationProjectBindingChanged() {
|
||||
loadConversationsWithGroups(conversationsSearchQuery);
|
||||
};
|
||||
|
||||
function getConversationSortBy() {
|
||||
try {
|
||||
@@ -6252,6 +6517,13 @@ async function fetchAllConversations(searchQuery) {
|
||||
}
|
||||
|
||||
function getConversationListEmptyHtml() {
|
||||
const filter = getConversationProjectFilter();
|
||||
if (filter && filter !== CONVERSATION_PROJECT_FILTER_NONE) {
|
||||
return '<div class="conversations-list-empty" data-i18n="chat.noProjectConversations"></div>';
|
||||
}
|
||||
if (filter === CONVERSATION_PROJECT_FILTER_NONE) {
|
||||
return '<div class="conversations-list-empty" data-i18n="chat.noUnboundConversations"></div>';
|
||||
}
|
||||
return '<div class="conversations-list-empty" data-i18n="chat.noHistoryConversations"></div>';
|
||||
}
|
||||
|
||||
@@ -6428,11 +6700,16 @@ async function loadConversationsWithGroups(searchQuery = '') {
|
||||
if (conversationSortBy === 'created_at') {
|
||||
convParams.set('sort_by', 'created_at');
|
||||
}
|
||||
const projectFilter = getConversationProjectFilter();
|
||||
if (projectFilter) {
|
||||
convParams.set('project_id', projectFilter);
|
||||
}
|
||||
if (searchQuery && searchQuery.trim()) {
|
||||
convParams.set('search', searchQuery.trim());
|
||||
} else {
|
||||
} else if (!projectFilter) {
|
||||
convParams.set('exclude_grouped', 'true');
|
||||
}
|
||||
updateConversationSidebarFilterUI();
|
||||
const url = `/api/conversations?${convParams}`;
|
||||
const [,, response] = await Promise.all([
|
||||
loadGroups(),
|
||||
@@ -6488,6 +6765,7 @@ async function loadConversationsWithGroups(searchQuery = '') {
|
||||
const pinnedConvs = [];
|
||||
const normalConvs = [];
|
||||
const hasSearchQuery = searchQuery && searchQuery.trim();
|
||||
const hasProjectFilter = !!getConversationProjectFilter();
|
||||
|
||||
uniqueConversations.forEach(conv => {
|
||||
// 如果有搜索关键词,显示所有匹配的对话(全局搜索,包括分组中的)
|
||||
@@ -6501,6 +6779,16 @@ async function loadConversationsWithGroups(searchQuery = '') {
|
||||
return;
|
||||
}
|
||||
|
||||
// 按项目筛选时展示该项目下全部对话(含分组内)
|
||||
if (hasProjectFilter) {
|
||||
if (conv.pinned) {
|
||||
pinnedConvs.push(conv);
|
||||
} else {
|
||||
normalConvs.push(conv);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果没有搜索关键词,使用原有逻辑
|
||||
// "最近对话"列表应该只显示不在任何分组中的对话
|
||||
// 无论是否在分组详情页,都不应该在"最近对话"中显示分组中的对话
|
||||
@@ -7731,6 +8019,84 @@ function closeContextMenu() {
|
||||
// 显示批量管理模态框
|
||||
let allConversationsForBatch = [];
|
||||
|
||||
function getConversationProjectId(conv) {
|
||||
return (conv?.projectId || conv?.project_id || '').trim();
|
||||
}
|
||||
|
||||
function getConversationProjectLabel(conv) {
|
||||
const pid = getConversationProjectId(conv);
|
||||
if (!pid) {
|
||||
return typeof window.t === 'function' ? window.t('batchManageModal.noProject') : '无项目';
|
||||
}
|
||||
return (window.projectNameById && window.projectNameById[pid]) || pid;
|
||||
}
|
||||
|
||||
async function refreshBatchProjectFilter() {
|
||||
const sel = document.getElementById('batch-project-filter');
|
||||
if (!sel) return;
|
||||
const saved = sel.value || '';
|
||||
if (typeof window.ensureProjectsLoaded === 'function') {
|
||||
try {
|
||||
await window.ensureProjectsLoaded();
|
||||
} catch (e) { /* ignore */ }
|
||||
}
|
||||
const tFn = typeof window.t === 'function' ? window.t.bind(window) : null;
|
||||
const allLabel = tFn ? tFn('chat.filterAllProjects') : '全部项目';
|
||||
const unboundLabel = tFn ? tFn('chat.filterUnboundProjects') : '未绑定项目';
|
||||
sel.innerHTML = '';
|
||||
const allOpt = document.createElement('option');
|
||||
allOpt.value = '';
|
||||
allOpt.textContent = allLabel;
|
||||
allOpt.setAttribute('data-i18n', 'chat.filterAllProjects');
|
||||
sel.appendChild(allOpt);
|
||||
const unboundOpt = document.createElement('option');
|
||||
unboundOpt.value = CONVERSATION_PROJECT_FILTER_NONE;
|
||||
unboundOpt.textContent = unboundLabel;
|
||||
unboundOpt.setAttribute('data-i18n', 'chat.filterUnboundProjects');
|
||||
sel.appendChild(unboundOpt);
|
||||
const source = window.projectNameById ? Object.keys(window.projectNameById) : [];
|
||||
source
|
||||
.sort((a, b) => {
|
||||
const na = (window.projectNameById[a] || a).toLowerCase();
|
||||
const nb = (window.projectNameById[b] || b).toLowerCase();
|
||||
return na.localeCompare(nb);
|
||||
})
|
||||
.forEach((id) => {
|
||||
const opt = document.createElement('option');
|
||||
opt.value = id;
|
||||
opt.textContent = window.projectNameById[id] || id;
|
||||
sel.appendChild(opt);
|
||||
});
|
||||
const valid = !saved || saved === CONVERSATION_PROJECT_FILTER_NONE || (window.projectNameById && window.projectNameById[saved]);
|
||||
sel.value = valid ? saved : '';
|
||||
syncProjectFilterCustomSelect(BATCH_PROJECT_FILTER_SELECT_ID);
|
||||
}
|
||||
|
||||
function getBatchFilteredConversations() {
|
||||
const query = (document.getElementById('batch-search-input')?.value || '').trim().toLowerCase();
|
||||
const projectFilter = (document.getElementById('batch-project-filter')?.value || '').trim();
|
||||
return allConversationsForBatch.filter((conv) => {
|
||||
const pid = getConversationProjectId(conv);
|
||||
if (projectFilter) {
|
||||
if (projectFilter === CONVERSATION_PROJECT_FILTER_NONE) {
|
||||
if (pid) return false;
|
||||
} else if (pid !== projectFilter) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!query) return true;
|
||||
const title = (conv.title || '').toLowerCase();
|
||||
const projectName = getConversationProjectLabel(conv).toLowerCase();
|
||||
return title.includes(query) || projectName.includes(query);
|
||||
});
|
||||
}
|
||||
|
||||
function applyBatchConversationFilters() {
|
||||
const filtered = getBatchFilteredConversations();
|
||||
updateBatchManageTitle(filtered.length);
|
||||
renderBatchConversations(filtered);
|
||||
}
|
||||
|
||||
// 更新批量管理模态框标题(含条数),支持 i18n;count 为当前条数
|
||||
function updateBatchManageTitle(count) {
|
||||
const titleEl = document.getElementById('batch-manage-title');
|
||||
@@ -7742,19 +8108,27 @@ function updateBatchManageTitle(count) {
|
||||
|
||||
async function showBatchManageModal() {
|
||||
try {
|
||||
initProjectFilterCustomSelect(BATCH_PROJECT_FILTER_SELECT_ID);
|
||||
allConversationsForBatch = await fetchAllConversations('');
|
||||
|
||||
const modal = document.getElementById('batch-manage-modal');
|
||||
updateBatchManageTitle(allConversationsForBatch.length);
|
||||
|
||||
renderBatchConversations();
|
||||
await refreshBatchProjectFilter();
|
||||
const sidebarFilter = getConversationProjectFilter();
|
||||
const batchSel = document.getElementById('batch-project-filter');
|
||||
if (batchSel && sidebarFilter && (
|
||||
sidebarFilter === CONVERSATION_PROJECT_FILTER_NONE ||
|
||||
(window.projectNameById && window.projectNameById[sidebarFilter])
|
||||
)) {
|
||||
batchSel.value = sidebarFilter;
|
||||
}
|
||||
const searchInput = document.getElementById('batch-search-input');
|
||||
if (searchInput) searchInput.value = '';
|
||||
applyBatchConversationFilters();
|
||||
openAppModal('batch-manage-modal', { focus: false });
|
||||
} catch (error) {
|
||||
console.error('加载对话列表失败:', error);
|
||||
// 错误时使用空数组,不显示错误提示(更友好的用户体验)
|
||||
initProjectFilterCustomSelect(BATCH_PROJECT_FILTER_SELECT_ID);
|
||||
allConversationsForBatch = [];
|
||||
updateBatchManageTitle(0);
|
||||
renderBatchConversations();
|
||||
await refreshBatchProjectFilter();
|
||||
applyBatchConversationFilters();
|
||||
openAppModal('batch-manage-modal', { focus: false });
|
||||
}
|
||||
}
|
||||
@@ -7817,15 +8191,27 @@ function renderBatchConversations(filtered = null) {
|
||||
checkbox.dataset.conversationId = conv.id;
|
||||
checkbox.addEventListener('change', syncSelectAllBatchCheckbox);
|
||||
|
||||
const checkboxCol = document.createElement('div');
|
||||
checkboxCol.className = 'batch-table-col-checkbox';
|
||||
checkboxCol.appendChild(checkbox);
|
||||
|
||||
const name = document.createElement('div');
|
||||
name.className = 'batch-table-col-name';
|
||||
const originalTitle = conv.title || (typeof window.t === 'function' ? window.t('batchManageModal.unnamedConversation') : '未命名对话');
|
||||
// 使用安全截断函数,限制最大长度为45个字符(留出空间显示省略号)
|
||||
const truncatedTitle = safeTruncateText(originalTitle, 45);
|
||||
const truncatedTitle = safeTruncateText(originalTitle, 36);
|
||||
name.textContent = truncatedTitle;
|
||||
// 设置title属性以显示完整文本(鼠标悬停时)
|
||||
name.title = originalTitle;
|
||||
|
||||
const project = document.createElement('div');
|
||||
project.className = 'batch-table-col-project';
|
||||
const projectLabel = getConversationProjectLabel(conv);
|
||||
const truncatedProject = safeTruncateText(projectLabel, 28);
|
||||
project.textContent = truncatedProject;
|
||||
project.title = projectLabel;
|
||||
if (!getConversationProjectId(conv)) {
|
||||
project.classList.add('is-unbound');
|
||||
}
|
||||
|
||||
const time = document.createElement('div');
|
||||
time.className = 'batch-table-col-time';
|
||||
const dateObj = conv.updatedAt ? new Date(conv.updatedAt) : new Date();
|
||||
@@ -7858,8 +8244,9 @@ function renderBatchConversations(filtered = null) {
|
||||
};
|
||||
action.appendChild(deleteBtn);
|
||||
|
||||
row.appendChild(checkbox);
|
||||
row.appendChild(checkboxCol);
|
||||
row.appendChild(name);
|
||||
row.appendChild(project);
|
||||
row.appendChild(time);
|
||||
row.appendChild(action);
|
||||
|
||||
@@ -7870,18 +8257,8 @@ function renderBatchConversations(filtered = null) {
|
||||
}
|
||||
|
||||
// 筛选批量管理对话
|
||||
function filterBatchConversations(query) {
|
||||
if (!query || !query.trim()) {
|
||||
renderBatchConversations();
|
||||
return;
|
||||
}
|
||||
|
||||
const filtered = allConversationsForBatch.filter(conv => {
|
||||
const title = (conv.title || '').toLowerCase();
|
||||
return title.includes(query.toLowerCase());
|
||||
});
|
||||
|
||||
renderBatchConversations(filtered);
|
||||
function filterBatchConversations() {
|
||||
applyBatchConversationFilters();
|
||||
}
|
||||
|
||||
// 全选/取消全选
|
||||
@@ -7958,6 +8335,10 @@ function closeBatchManageModal() {
|
||||
selectAll.checked = false;
|
||||
selectAll.indeterminate = false;
|
||||
}
|
||||
const searchInput = document.getElementById('batch-search-input');
|
||||
if (searchInput) searchInput.value = '';
|
||||
const batchProj = document.getElementById('batch-project-filter');
|
||||
if (batchProj) batchProj.value = '';
|
||||
allConversationsForBatch = [];
|
||||
}
|
||||
|
||||
@@ -8030,7 +8411,7 @@ document.addEventListener('languagechange', function () {
|
||||
refreshChatPanelI18n();
|
||||
const modal = document.getElementById('batch-manage-modal');
|
||||
if (isAppModalOpen('batch-manage-modal')) {
|
||||
updateBatchManageTitle(allConversationsForBatch.length);
|
||||
refreshBatchProjectFilter().then(() => applyBatchConversationFilters());
|
||||
}
|
||||
// 侧边栏最近对话等列表的时间戳会随语言变化(24h/12h 等),重新拉列表以统一格式
|
||||
if (typeof loadConversationsWithGroups === 'function') {
|
||||
@@ -8962,6 +9343,8 @@ function clearGroupSearch() {
|
||||
// 初始化时加载分组
|
||||
document.addEventListener('DOMContentLoaded', async () => {
|
||||
updateConversationSortMenuUI();
|
||||
initConversationProjectCustomSelect();
|
||||
await refreshConversationProjectFilter();
|
||||
await loadGroups();
|
||||
await loadConversationsWithGroups();
|
||||
|
||||
@@ -9018,8 +9401,16 @@ document.addEventListener('DOMContentLoaded', async () => {
|
||||
});
|
||||
});
|
||||
|
||||
async function refreshAllProjectFilterSelects() {
|
||||
await refreshConversationProjectFilter();
|
||||
await refreshBatchProjectFilter();
|
||||
}
|
||||
|
||||
// 顶层 async function 不会自动挂到 window,hitl 等脚本依赖 window.loadConversation
|
||||
if (typeof window !== 'undefined') {
|
||||
window.loadConversation = loadConversation;
|
||||
window.startNewConversation = startNewConversation;
|
||||
window.refreshConversationProjectFilter = refreshConversationProjectFilter;
|
||||
window.refreshAllProjectFilterSelects = refreshAllProjectFilterSelects;
|
||||
window.onConversationProjectFilterChange = onConversationProjectFilterChange;
|
||||
}
|
||||
|
||||
+42
-11
@@ -3547,6 +3547,7 @@ const monitorState = {
|
||||
timelineLoading: false,
|
||||
lastFetchedAt: null,
|
||||
retentionDays: 0,
|
||||
selectedExecutions: new Set(),
|
||||
pagination: {
|
||||
page: 1,
|
||||
pageSize: (() => {
|
||||
@@ -5063,10 +5064,12 @@ function renderMonitorExecutions(executions = [], statusFilter = 'all') {
|
||||
const terminateBtn = status === 'running'
|
||||
? `<button type="button" class="btn-secondary btn-monitor-abort" onclick="cancelMCPToolExecution('${rawExecId.replace(/\\/g, '\\\\').replace(/'/g, "\\'")}')">${escapeHtml(terminateLabel)}</button>`
|
||||
: '';
|
||||
const jsExecId = rawExecId.replace(/\\/g, '\\\\').replace(/'/g, "\\'");
|
||||
const isSelected = monitorState.selectedExecutions.has(rawExecId);
|
||||
return `
|
||||
<tr>
|
||||
<td>
|
||||
<input type="checkbox" class="monitor-execution-checkbox" value="${executionId}" onchange="updateBatchActionsState()" />
|
||||
<input type="checkbox" class="monitor-execution-checkbox" value="${executionId}" ${isSelected ? 'checked' : ''} onchange="toggleExecutionSelection('${jsExecId}', this.checked)" />
|
||||
</td>
|
||||
<td>${toolName}</td>
|
||||
<td><span class="${statusClass}">${escapeHtml(statusLabel)}</span></td>
|
||||
@@ -5212,6 +5215,8 @@ async function deleteExecution(executionId) {
|
||||
throw new Error(error.error || deleteFailedMsg);
|
||||
}
|
||||
|
||||
monitorState.selectedExecutions.delete(executionId);
|
||||
|
||||
// 删除成功后刷新当前页面
|
||||
const currentPage = monitorState.pagination.page;
|
||||
await refreshMonitorPanel(currentPage);
|
||||
@@ -5225,10 +5230,22 @@ async function deleteExecution(executionId) {
|
||||
}
|
||||
}
|
||||
|
||||
// 切换单条执行记录选中状态(持久化到 monitorState,避免轮询刷新后丢失)
|
||||
function toggleExecutionSelection(executionId, selected) {
|
||||
if (!executionId) {
|
||||
return;
|
||||
}
|
||||
if (selected) {
|
||||
monitorState.selectedExecutions.add(executionId);
|
||||
} else {
|
||||
monitorState.selectedExecutions.delete(executionId);
|
||||
}
|
||||
updateBatchActionsState();
|
||||
}
|
||||
|
||||
// 更新批量操作状态
|
||||
function updateBatchActionsState() {
|
||||
const checkboxes = document.querySelectorAll('.monitor-execution-checkbox:checked');
|
||||
const selectedCount = checkboxes.length;
|
||||
const selectedCount = monitorState.selectedExecutions.size;
|
||||
const batchActions = document.getElementById('monitor-batch-actions');
|
||||
const selectedCountSpan = document.getElementById('monitor-selected-count');
|
||||
|
||||
@@ -5245,13 +5262,18 @@ function updateBatchActionsState() {
|
||||
selectedCountSpan.textContent = typeof window.t === 'function' ? window.t('mcp.selectedCount', { count: selectedCount }) : '已选择 ' + selectedCount + ' 项';
|
||||
}
|
||||
|
||||
// 更新全选复选框状态
|
||||
// 更新全选复选框状态(仅反映当前页)
|
||||
const selectAllCheckbox = document.getElementById('monitor-select-all');
|
||||
if (selectAllCheckbox) {
|
||||
const allCheckboxes = document.querySelectorAll('.monitor-execution-checkbox');
|
||||
const allChecked = allCheckboxes.length > 0 && Array.from(allCheckboxes).every(cb => cb.checked);
|
||||
selectAllCheckbox.checked = allChecked;
|
||||
selectAllCheckbox.indeterminate = selectedCount > 0 && selectedCount < allCheckboxes.length;
|
||||
if (allCheckboxes.length === 0) {
|
||||
selectAllCheckbox.checked = false;
|
||||
selectAllCheckbox.indeterminate = false;
|
||||
} else {
|
||||
const checkedOnPage = Array.from(allCheckboxes).filter(cb => monitorState.selectedExecutions.has(cb.value)).length;
|
||||
selectAllCheckbox.checked = checkedOnPage === allCheckboxes.length;
|
||||
selectAllCheckbox.indeterminate = checkedOnPage > 0 && checkedOnPage < allCheckboxes.length;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5260,6 +5282,11 @@ function toggleSelectAll(checkbox) {
|
||||
const checkboxes = document.querySelectorAll('.monitor-execution-checkbox');
|
||||
checkboxes.forEach(cb => {
|
||||
cb.checked = checkbox.checked;
|
||||
if (checkbox.checked) {
|
||||
monitorState.selectedExecutions.add(cb.value);
|
||||
} else {
|
||||
monitorState.selectedExecutions.delete(cb.value);
|
||||
}
|
||||
});
|
||||
updateBatchActionsState();
|
||||
}
|
||||
@@ -5269,6 +5296,7 @@ function selectAllExecutions() {
|
||||
const checkboxes = document.querySelectorAll('.monitor-execution-checkbox');
|
||||
checkboxes.forEach(cb => {
|
||||
cb.checked = true;
|
||||
monitorState.selectedExecutions.add(cb.value);
|
||||
});
|
||||
const selectAllCheckbox = document.getElementById('monitor-select-all');
|
||||
if (selectAllCheckbox) {
|
||||
@@ -5284,6 +5312,7 @@ function deselectAllExecutions() {
|
||||
checkboxes.forEach(cb => {
|
||||
cb.checked = false;
|
||||
});
|
||||
monitorState.selectedExecutions.clear();
|
||||
const selectAllCheckbox = document.getElementById('monitor-select-all');
|
||||
if (selectAllCheckbox) {
|
||||
selectAllCheckbox.checked = false;
|
||||
@@ -5294,14 +5323,12 @@ function deselectAllExecutions() {
|
||||
|
||||
// 批量删除执行记录
|
||||
async function batchDeleteExecutions() {
|
||||
const checkboxes = document.querySelectorAll('.monitor-execution-checkbox:checked');
|
||||
if (checkboxes.length === 0) {
|
||||
const ids = Array.from(monitorState.selectedExecutions);
|
||||
if (ids.length === 0) {
|
||||
const selectFirstMsg = typeof window.t === 'function' ? window.t('mcpMonitor.selectExecFirst') : '请先选择要删除的执行记录';
|
||||
alert(selectFirstMsg);
|
||||
return;
|
||||
}
|
||||
|
||||
const ids = Array.from(checkboxes).map(cb => cb.value);
|
||||
const count = ids.length;
|
||||
const batchConfirmMsg = typeof window.t === 'function' ? window.t('mcpMonitor.batchDeleteConfirm', { count: count }) : `确定要删除选中的 ${count} 条执行记录吗?此操作不可恢复。`;
|
||||
if (!confirm(batchConfirmMsg)) {
|
||||
@@ -5325,6 +5352,10 @@ async function batchDeleteExecutions() {
|
||||
|
||||
const result = await response.json().catch(() => ({}));
|
||||
const deletedCount = result.deleted || count;
|
||||
|
||||
ids.forEach(function (id) {
|
||||
monitorState.selectedExecutions.delete(id);
|
||||
});
|
||||
|
||||
// 删除成功后刷新当前页面
|
||||
const currentPage = monitorState.pagination.page;
|
||||
|
||||
@@ -293,6 +293,9 @@ async function ensureProjectsLoaded(force) {
|
||||
projectsCacheAll = list;
|
||||
rebuildProjectNameMap(projectsCacheAll);
|
||||
_projectsListReady = true;
|
||||
if (typeof window.refreshConversationProjectFilter === 'function') {
|
||||
window.refreshConversationProjectFilter();
|
||||
}
|
||||
return projectsCacheAll;
|
||||
})
|
||||
.catch((e) => {
|
||||
@@ -371,6 +374,9 @@ async function loadProjectsList() {
|
||||
if (typeof refreshVulnerabilityProjectFilter === 'function') {
|
||||
refreshVulnerabilityProjectFilter();
|
||||
}
|
||||
if (typeof window.refreshAllProjectFilterSelects === 'function') {
|
||||
await window.refreshAllProjectFilterSelects();
|
||||
}
|
||||
}
|
||||
|
||||
function projectInitial(name) {
|
||||
@@ -2198,6 +2204,9 @@ async function applyChatProjectSelection(projectId) {
|
||||
setActiveProjectId(projectId);
|
||||
}
|
||||
updateChatProjectButtonLabel();
|
||||
if (typeof window.onConversationProjectBindingChanged === 'function') {
|
||||
window.onConversationProjectBindingChanged(projectId);
|
||||
}
|
||||
}
|
||||
|
||||
/** 对话页项目选择器:同步按钮文案;若浮层已打开则刷新列表 */
|
||||
@@ -2326,3 +2335,4 @@ window.focusProjectFactGraphEdge = focusProjectFactGraphEdge;
|
||||
window.toggleProjectFactGraphConnectMode = toggleProjectFactGraphConnectMode;
|
||||
window.rebuildProjectNameMap = rebuildProjectNameMap;
|
||||
window.projectNameById = projectNameById;
|
||||
window.ensureProjectsLoaded = ensureProjectsLoaded;
|
||||
|
||||
@@ -778,7 +778,7 @@
|
||||
</div>
|
||||
<div class="sidebar-content">
|
||||
<!-- 全局搜索 -->
|
||||
<div class="conversation-search-box" style="margin-bottom: 16px; margin-top: 16px;">
|
||||
<div class="conversation-search-box">
|
||||
<input type="text" id="conversation-search-input" data-i18n="chat.searchHistory" data-i18n-attr="placeholder" placeholder="搜索历史记录..."
|
||||
oninput="handleConversationSearch(this.value)"
|
||||
onkeypress="if(event.key === 'Enter') handleConversationSearch(this.value)" />
|
||||
@@ -790,6 +790,15 @@
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- 按项目筛选对话 -->
|
||||
<div class="conversation-project-filter">
|
||||
<label class="conversation-project-filter-label" for="conversation-project-filter" data-i18n="chat.filterByProject">按项目筛选</label>
|
||||
<select id="conversation-project-filter" class="conversation-project-filter-native" onchange="onConversationProjectFilterChange(this.value)" data-i18n="chat.filterByProject" data-i18n-attr="title" title="按项目筛选">
|
||||
<option value="" data-i18n="chat.filterAllProjects">全部项目</option>
|
||||
<option value="__none__" data-i18n="chat.filterUnboundProjects">未绑定项目</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- 对话分组 -->
|
||||
<div class="conversation-groups-section">
|
||||
@@ -3764,6 +3773,10 @@
|
||||
<div class="modal-header">
|
||||
<h2 id="batch-manage-title">管理对话记录·共<span id="batch-manage-count">0</span>条</h2>
|
||||
<div class="batch-manage-header-actions">
|
||||
<select id="batch-project-filter" class="conversation-project-filter-native" onchange="applyBatchConversationFilters()" data-i18n="batchManageModal.filterByProject" data-i18n-attr="title" title="按项目筛选">
|
||||
<option value="" data-i18n="chat.filterAllProjects">全部项目</option>
|
||||
<option value="__none__" data-i18n="chat.filterUnboundProjects">未绑定项目</option>
|
||||
</select>
|
||||
<div class="batch-search-box">
|
||||
<input type="text" id="batch-search-input" data-i18n="batchManageModal.searchPlaceholder" data-i18n-attr="placeholder" placeholder="搜索历史记录" oninput="filterBatchConversations(this.value)" />
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
@@ -3781,6 +3794,7 @@
|
||||
<input type="checkbox" id="batch-select-all" onchange="toggleSelectAllBatch()" data-i18n="batchManageModal.selectAll" data-i18n-attr="title" title="全选" />
|
||||
</div>
|
||||
<div class="batch-table-col-name" data-i18n="batchManageModal.conversationName">对话名称</div>
|
||||
<div class="batch-table-col-project" data-i18n="batchManageModal.project">项目</div>
|
||||
<div class="batch-table-col-time" data-i18n="batchManageModal.lastTime">最近一次对话时间</div>
|
||||
<div class="batch-table-col-action" data-i18n="batchManageModal.action">操作</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user