mirror of
https://github.com/Ed1s0nZ/CyberStrikeAI.git
synced 2026-07-09 22:08:52 +02:00
Add files via upload
This commit is contained in:
+243
-6
@@ -1615,9 +1615,34 @@ header {
|
|||||||
|
|
||||||
.conversation-search-box {
|
.conversation-search-box {
|
||||||
position: relative;
|
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;
|
margin-bottom: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.conversation-sidebar .recent-conversations-section {
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.conversation-sidebar .section-header {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
.conversation-search-box input {
|
.conversation-search-box input {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 8px 32px 8px 12px;
|
padding: 8px 32px 8px 12px;
|
||||||
@@ -1668,6 +1693,170 @@ header {
|
|||||||
height: 14px;
|
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 {
|
.conversations-list {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@@ -11196,6 +11385,7 @@ tr.mcp-stats-tool-row[data-tool-name]:focus-visible {
|
|||||||
.conversation-groups-section,
|
.conversation-groups-section,
|
||||||
.recent-conversations-section {
|
.recent-conversations-section {
|
||||||
margin-bottom: 24px;
|
margin-bottom: 24px;
|
||||||
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.conversation-groups-section:last-child,
|
.conversation-groups-section:last-child,
|
||||||
@@ -11209,6 +11399,8 @@ tr.mcp-stats-tool-row[data-tool-name]:focus-visible {
|
|||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
margin-bottom: 12px;
|
margin-bottom: 12px;
|
||||||
padding: 0 8px;
|
padding: 0 8px;
|
||||||
|
min-width: 0;
|
||||||
|
gap: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.section-header-actions {
|
.section-header-actions {
|
||||||
@@ -11337,6 +11529,21 @@ tr.mcp-stats-tool-row[data-tool-name]:focus-visible {
|
|||||||
letter-spacing: 0.5px;
|
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,
|
.add-group-btn,
|
||||||
.batch-manage-btn {
|
.batch-manage-btn {
|
||||||
width: 24px;
|
width: 24px;
|
||||||
@@ -11729,7 +11936,7 @@ tr.mcp-stats-tool-row[data-tool-name]:focus-visible {
|
|||||||
|
|
||||||
/* 批量管理模态框 */
|
/* 批量管理模态框 */
|
||||||
.batch-manage-modal-content {
|
.batch-manage-modal-content {
|
||||||
max-width: 800px;
|
max-width: 920px;
|
||||||
width: 90vw;
|
width: 90vw;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@@ -11739,7 +11946,23 @@ tr.mcp-stats-tool-row[data-tool-name]:focus-visible {
|
|||||||
.batch-manage-header-actions {
|
.batch-manage-header-actions {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
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 {
|
.batch-search-box {
|
||||||
@@ -11783,8 +12006,8 @@ tr.mcp-stats-tool-row[data-tool-name]:focus-visible {
|
|||||||
|
|
||||||
.batch-table-header {
|
.batch-table-header {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 40px 1fr 180px 80px;
|
grid-template-columns: 40px minmax(0, 1.2fr) minmax(0, 0.9fr) 160px 72px;
|
||||||
gap: 16px;
|
gap: 12px;
|
||||||
padding: 12px 16px;
|
padding: 12px 16px;
|
||||||
background: var(--bg-secondary);
|
background: var(--bg-secondary);
|
||||||
border-bottom: 1px solid var(--border-color);
|
border-bottom: 1px solid var(--border-color);
|
||||||
@@ -11802,8 +12025,8 @@ tr.mcp-stats-tool-row[data-tool-name]:focus-visible {
|
|||||||
|
|
||||||
.batch-conversation-row {
|
.batch-conversation-row {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 40px 1fr 180px 80px;
|
grid-template-columns: 40px minmax(0, 1.2fr) minmax(0, 0.9fr) 160px 72px;
|
||||||
gap: 16px;
|
gap: 12px;
|
||||||
padding: 12px 16px;
|
padding: 12px 16px;
|
||||||
border-bottom: 1px solid var(--border-color);
|
border-bottom: 1px solid var(--border-color);
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -11830,6 +12053,20 @@ tr.mcp-stats-tool-row[data-tool-name]:focus-visible {
|
|||||||
/* 完全依赖JavaScript截断,禁用CSS的ellipsis以避免在UTF-8多字节字符中间截断 */
|
/* 完全依赖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 {
|
.batch-table-col-time {
|
||||||
font-size: 0.875rem;
|
font-size: 0.875rem;
|
||||||
color: var(--text-muted);
|
color: var(--text-muted);
|
||||||
|
|||||||
@@ -499,6 +499,13 @@
|
|||||||
"conversationGroups": "Conversation groups",
|
"conversationGroups": "Conversation groups",
|
||||||
"addGroup": "New group",
|
"addGroup": "New group",
|
||||||
"recentConversations": "Recent conversations",
|
"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",
|
"sortConversations": "Sort",
|
||||||
"sortByCreatedAt": "Created time",
|
"sortByCreatedAt": "Created time",
|
||||||
"sortByUpdatedAt": "Updated time",
|
"sortByUpdatedAt": "Updated time",
|
||||||
@@ -2527,6 +2534,9 @@
|
|||||||
"title": "Manage conversations · {{count}} total",
|
"title": "Manage conversations · {{count}} total",
|
||||||
"searchPlaceholder": "Search history",
|
"searchPlaceholder": "Search history",
|
||||||
"conversationName": "Conversation name",
|
"conversationName": "Conversation name",
|
||||||
|
"project": "Project",
|
||||||
|
"noProject": "No project",
|
||||||
|
"filterByProject": "Filter by project",
|
||||||
"lastTime": "Last activity",
|
"lastTime": "Last activity",
|
||||||
"action": "Action",
|
"action": "Action",
|
||||||
"selectAll": "Select all",
|
"selectAll": "Select all",
|
||||||
|
|||||||
@@ -487,6 +487,13 @@
|
|||||||
"conversationGroups": "对话分组",
|
"conversationGroups": "对话分组",
|
||||||
"addGroup": "新建分组",
|
"addGroup": "新建分组",
|
||||||
"recentConversations": "最近对话",
|
"recentConversations": "最近对话",
|
||||||
|
"filterByProject": "按项目筛选",
|
||||||
|
"filterAllProjects": "全部项目",
|
||||||
|
"filterUnboundProjects": "未绑定项目",
|
||||||
|
"projectConversationsTitle": "{{name}} · 对话",
|
||||||
|
"unboundConversationsTitle": "未绑定项目",
|
||||||
|
"noProjectConversations": "该项目暂无对话",
|
||||||
|
"noUnboundConversations": "暂无未绑定项目的对话",
|
||||||
"sortConversations": "排序",
|
"sortConversations": "排序",
|
||||||
"sortByCreatedAt": "创建时间",
|
"sortByCreatedAt": "创建时间",
|
||||||
"sortByUpdatedAt": "更新时间",
|
"sortByUpdatedAt": "更新时间",
|
||||||
@@ -2515,6 +2522,9 @@
|
|||||||
"title": "管理对话记录·共{{count}}条",
|
"title": "管理对话记录·共{{count}}条",
|
||||||
"searchPlaceholder": "搜索历史记录",
|
"searchPlaceholder": "搜索历史记录",
|
||||||
"conversationName": "对话名称",
|
"conversationName": "对话名称",
|
||||||
|
"project": "项目",
|
||||||
|
"noProject": "无项目",
|
||||||
|
"filterByProject": "按项目筛选",
|
||||||
"lastTime": "最近一次对话时间",
|
"lastTime": "最近一次对话时间",
|
||||||
"action": "操作",
|
"action": "操作",
|
||||||
"selectAll": "全选",
|
"selectAll": "全选",
|
||||||
|
|||||||
+425
-34
@@ -3322,6 +3322,18 @@ function createConversationListItem(conversation) {
|
|||||||
title.title = titleText; // 设置完整标题以便悬停查看
|
title.title = titleText; // 设置完整标题以便悬停查看
|
||||||
contentWrapper.appendChild(title);
|
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');
|
const time = document.createElement('div');
|
||||||
time.className = 'conversation-time';
|
time.className = 'conversation-time';
|
||||||
time.textContent = conversation._timeText || formatConversationTimestamp(conversation._time || new Date());
|
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');
|
const batchModal = document.getElementById('batch-manage-modal');
|
||||||
if (batchModal && isAppModalOpen('batch-manage-modal')) {
|
if (batchModal && isAppModalOpen('batch-manage-modal')) {
|
||||||
allConversationsForBatch = allConversationsForBatch.filter(c => c.id !== conversationId);
|
allConversationsForBatch = allConversationsForBatch.filter(c => c.id !== conversationId);
|
||||||
updateBatchManageTitle(allConversationsForBatch.length);
|
applyBatchConversationFilters();
|
||||||
const searchInput = document.getElementById('batch-search-input');
|
|
||||||
const query = searchInput ? searchInput.value : '';
|
|
||||||
if (query && query.trim()) {
|
|
||||||
filterBatchConversations(query);
|
|
||||||
} else {
|
|
||||||
renderBatchConversations();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 通知其他模块(如 WebShell AI 助手)同步删除,保持列表一致
|
// 通知其他模块(如 WebShell AI 助手)同步删除,保持列表一致
|
||||||
@@ -6075,6 +6080,266 @@ let pendingGroupMappings = {}; // 待保留的分组映射(用于处理后端A
|
|||||||
let conversationsListLoadSeq = 0; // 对话列表加载序号,避免并发请求导致重复渲染
|
let conversationsListLoadSeq = 0; // 对话列表加载序号,避免并发请求导致重复渲染
|
||||||
const CONVERSATIONS_PAGE_SIZE_KEY = 'cyberstrike.conversations_page_size';
|
const CONVERSATIONS_PAGE_SIZE_KEY = 'cyberstrike.conversations_page_size';
|
||||||
const CONVERSATIONS_SORT_KEY = 'cyberstrike.conversations_sort_by';
|
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() {
|
function getConversationSortBy() {
|
||||||
try {
|
try {
|
||||||
@@ -6252,6 +6517,13 @@ async function fetchAllConversations(searchQuery) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getConversationListEmptyHtml() {
|
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>';
|
return '<div class="conversations-list-empty" data-i18n="chat.noHistoryConversations"></div>';
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -6428,11 +6700,16 @@ async function loadConversationsWithGroups(searchQuery = '') {
|
|||||||
if (conversationSortBy === 'created_at') {
|
if (conversationSortBy === 'created_at') {
|
||||||
convParams.set('sort_by', 'created_at');
|
convParams.set('sort_by', 'created_at');
|
||||||
}
|
}
|
||||||
|
const projectFilter = getConversationProjectFilter();
|
||||||
|
if (projectFilter) {
|
||||||
|
convParams.set('project_id', projectFilter);
|
||||||
|
}
|
||||||
if (searchQuery && searchQuery.trim()) {
|
if (searchQuery && searchQuery.trim()) {
|
||||||
convParams.set('search', searchQuery.trim());
|
convParams.set('search', searchQuery.trim());
|
||||||
} else {
|
} else if (!projectFilter) {
|
||||||
convParams.set('exclude_grouped', 'true');
|
convParams.set('exclude_grouped', 'true');
|
||||||
}
|
}
|
||||||
|
updateConversationSidebarFilterUI();
|
||||||
const url = `/api/conversations?${convParams}`;
|
const url = `/api/conversations?${convParams}`;
|
||||||
const [,, response] = await Promise.all([
|
const [,, response] = await Promise.all([
|
||||||
loadGroups(),
|
loadGroups(),
|
||||||
@@ -6488,6 +6765,7 @@ async function loadConversationsWithGroups(searchQuery = '') {
|
|||||||
const pinnedConvs = [];
|
const pinnedConvs = [];
|
||||||
const normalConvs = [];
|
const normalConvs = [];
|
||||||
const hasSearchQuery = searchQuery && searchQuery.trim();
|
const hasSearchQuery = searchQuery && searchQuery.trim();
|
||||||
|
const hasProjectFilter = !!getConversationProjectFilter();
|
||||||
|
|
||||||
uniqueConversations.forEach(conv => {
|
uniqueConversations.forEach(conv => {
|
||||||
// 如果有搜索关键词,显示所有匹配的对话(全局搜索,包括分组中的)
|
// 如果有搜索关键词,显示所有匹配的对话(全局搜索,包括分组中的)
|
||||||
@@ -6501,6 +6779,16 @@ async function loadConversationsWithGroups(searchQuery = '') {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 按项目筛选时展示该项目下全部对话(含分组内)
|
||||||
|
if (hasProjectFilter) {
|
||||||
|
if (conv.pinned) {
|
||||||
|
pinnedConvs.push(conv);
|
||||||
|
} else {
|
||||||
|
normalConvs.push(conv);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// 如果没有搜索关键词,使用原有逻辑
|
// 如果没有搜索关键词,使用原有逻辑
|
||||||
// "最近对话"列表应该只显示不在任何分组中的对话
|
// "最近对话"列表应该只显示不在任何分组中的对话
|
||||||
// 无论是否在分组详情页,都不应该在"最近对话"中显示分组中的对话
|
// 无论是否在分组详情页,都不应该在"最近对话"中显示分组中的对话
|
||||||
@@ -7731,6 +8019,84 @@ function closeContextMenu() {
|
|||||||
// 显示批量管理模态框
|
// 显示批量管理模态框
|
||||||
let allConversationsForBatch = [];
|
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 为当前条数
|
// 更新批量管理模态框标题(含条数),支持 i18n;count 为当前条数
|
||||||
function updateBatchManageTitle(count) {
|
function updateBatchManageTitle(count) {
|
||||||
const titleEl = document.getElementById('batch-manage-title');
|
const titleEl = document.getElementById('batch-manage-title');
|
||||||
@@ -7742,19 +8108,27 @@ function updateBatchManageTitle(count) {
|
|||||||
|
|
||||||
async function showBatchManageModal() {
|
async function showBatchManageModal() {
|
||||||
try {
|
try {
|
||||||
|
initProjectFilterCustomSelect(BATCH_PROJECT_FILTER_SELECT_ID);
|
||||||
allConversationsForBatch = await fetchAllConversations('');
|
allConversationsForBatch = await fetchAllConversations('');
|
||||||
|
await refreshBatchProjectFilter();
|
||||||
const modal = document.getElementById('batch-manage-modal');
|
const sidebarFilter = getConversationProjectFilter();
|
||||||
updateBatchManageTitle(allConversationsForBatch.length);
|
const batchSel = document.getElementById('batch-project-filter');
|
||||||
|
if (batchSel && sidebarFilter && (
|
||||||
renderBatchConversations();
|
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 });
|
openAppModal('batch-manage-modal', { focus: false });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('加载对话列表失败:', error);
|
console.error('加载对话列表失败:', error);
|
||||||
// 错误时使用空数组,不显示错误提示(更友好的用户体验)
|
initProjectFilterCustomSelect(BATCH_PROJECT_FILTER_SELECT_ID);
|
||||||
allConversationsForBatch = [];
|
allConversationsForBatch = [];
|
||||||
updateBatchManageTitle(0);
|
await refreshBatchProjectFilter();
|
||||||
renderBatchConversations();
|
applyBatchConversationFilters();
|
||||||
openAppModal('batch-manage-modal', { focus: false });
|
openAppModal('batch-manage-modal', { focus: false });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -7817,15 +8191,27 @@ function renderBatchConversations(filtered = null) {
|
|||||||
checkbox.dataset.conversationId = conv.id;
|
checkbox.dataset.conversationId = conv.id;
|
||||||
checkbox.addEventListener('change', syncSelectAllBatchCheckbox);
|
checkbox.addEventListener('change', syncSelectAllBatchCheckbox);
|
||||||
|
|
||||||
|
const checkboxCol = document.createElement('div');
|
||||||
|
checkboxCol.className = 'batch-table-col-checkbox';
|
||||||
|
checkboxCol.appendChild(checkbox);
|
||||||
|
|
||||||
const name = document.createElement('div');
|
const name = document.createElement('div');
|
||||||
name.className = 'batch-table-col-name';
|
name.className = 'batch-table-col-name';
|
||||||
const originalTitle = conv.title || (typeof window.t === 'function' ? window.t('batchManageModal.unnamedConversation') : '未命名对话');
|
const originalTitle = conv.title || (typeof window.t === 'function' ? window.t('batchManageModal.unnamedConversation') : '未命名对话');
|
||||||
// 使用安全截断函数,限制最大长度为45个字符(留出空间显示省略号)
|
const truncatedTitle = safeTruncateText(originalTitle, 36);
|
||||||
const truncatedTitle = safeTruncateText(originalTitle, 45);
|
|
||||||
name.textContent = truncatedTitle;
|
name.textContent = truncatedTitle;
|
||||||
// 设置title属性以显示完整文本(鼠标悬停时)
|
|
||||||
name.title = originalTitle;
|
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');
|
const time = document.createElement('div');
|
||||||
time.className = 'batch-table-col-time';
|
time.className = 'batch-table-col-time';
|
||||||
const dateObj = conv.updatedAt ? new Date(conv.updatedAt) : new Date();
|
const dateObj = conv.updatedAt ? new Date(conv.updatedAt) : new Date();
|
||||||
@@ -7858,8 +8244,9 @@ function renderBatchConversations(filtered = null) {
|
|||||||
};
|
};
|
||||||
action.appendChild(deleteBtn);
|
action.appendChild(deleteBtn);
|
||||||
|
|
||||||
row.appendChild(checkbox);
|
row.appendChild(checkboxCol);
|
||||||
row.appendChild(name);
|
row.appendChild(name);
|
||||||
|
row.appendChild(project);
|
||||||
row.appendChild(time);
|
row.appendChild(time);
|
||||||
row.appendChild(action);
|
row.appendChild(action);
|
||||||
|
|
||||||
@@ -7870,18 +8257,8 @@ function renderBatchConversations(filtered = null) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 筛选批量管理对话
|
// 筛选批量管理对话
|
||||||
function filterBatchConversations(query) {
|
function filterBatchConversations() {
|
||||||
if (!query || !query.trim()) {
|
applyBatchConversationFilters();
|
||||||
renderBatchConversations();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const filtered = allConversationsForBatch.filter(conv => {
|
|
||||||
const title = (conv.title || '').toLowerCase();
|
|
||||||
return title.includes(query.toLowerCase());
|
|
||||||
});
|
|
||||||
|
|
||||||
renderBatchConversations(filtered);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 全选/取消全选
|
// 全选/取消全选
|
||||||
@@ -7958,6 +8335,10 @@ function closeBatchManageModal() {
|
|||||||
selectAll.checked = false;
|
selectAll.checked = false;
|
||||||
selectAll.indeterminate = 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 = [];
|
allConversationsForBatch = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -8030,7 +8411,7 @@ document.addEventListener('languagechange', function () {
|
|||||||
refreshChatPanelI18n();
|
refreshChatPanelI18n();
|
||||||
const modal = document.getElementById('batch-manage-modal');
|
const modal = document.getElementById('batch-manage-modal');
|
||||||
if (isAppModalOpen('batch-manage-modal')) {
|
if (isAppModalOpen('batch-manage-modal')) {
|
||||||
updateBatchManageTitle(allConversationsForBatch.length);
|
refreshBatchProjectFilter().then(() => applyBatchConversationFilters());
|
||||||
}
|
}
|
||||||
// 侧边栏最近对话等列表的时间戳会随语言变化(24h/12h 等),重新拉列表以统一格式
|
// 侧边栏最近对话等列表的时间戳会随语言变化(24h/12h 等),重新拉列表以统一格式
|
||||||
if (typeof loadConversationsWithGroups === 'function') {
|
if (typeof loadConversationsWithGroups === 'function') {
|
||||||
@@ -8962,6 +9343,8 @@ function clearGroupSearch() {
|
|||||||
// 初始化时加载分组
|
// 初始化时加载分组
|
||||||
document.addEventListener('DOMContentLoaded', async () => {
|
document.addEventListener('DOMContentLoaded', async () => {
|
||||||
updateConversationSortMenuUI();
|
updateConversationSortMenuUI();
|
||||||
|
initConversationProjectCustomSelect();
|
||||||
|
await refreshConversationProjectFilter();
|
||||||
await loadGroups();
|
await loadGroups();
|
||||||
await loadConversationsWithGroups();
|
await loadConversationsWithGroups();
|
||||||
|
|
||||||
@@ -9018,8 +9401,16 @@ document.addEventListener('DOMContentLoaded', async () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
async function refreshAllProjectFilterSelects() {
|
||||||
|
await refreshConversationProjectFilter();
|
||||||
|
await refreshBatchProjectFilter();
|
||||||
|
}
|
||||||
|
|
||||||
// 顶层 async function 不会自动挂到 window,hitl 等脚本依赖 window.loadConversation
|
// 顶层 async function 不会自动挂到 window,hitl 等脚本依赖 window.loadConversation
|
||||||
if (typeof window !== 'undefined') {
|
if (typeof window !== 'undefined') {
|
||||||
window.loadConversation = loadConversation;
|
window.loadConversation = loadConversation;
|
||||||
window.startNewConversation = startNewConversation;
|
window.startNewConversation = startNewConversation;
|
||||||
|
window.refreshConversationProjectFilter = refreshConversationProjectFilter;
|
||||||
|
window.refreshAllProjectFilterSelects = refreshAllProjectFilterSelects;
|
||||||
|
window.onConversationProjectFilterChange = onConversationProjectFilterChange;
|
||||||
}
|
}
|
||||||
|
|||||||
+42
-11
@@ -3547,6 +3547,7 @@ const monitorState = {
|
|||||||
timelineLoading: false,
|
timelineLoading: false,
|
||||||
lastFetchedAt: null,
|
lastFetchedAt: null,
|
||||||
retentionDays: 0,
|
retentionDays: 0,
|
||||||
|
selectedExecutions: new Set(),
|
||||||
pagination: {
|
pagination: {
|
||||||
page: 1,
|
page: 1,
|
||||||
pageSize: (() => {
|
pageSize: (() => {
|
||||||
@@ -5063,10 +5064,12 @@ function renderMonitorExecutions(executions = [], statusFilter = 'all') {
|
|||||||
const terminateBtn = status === 'running'
|
const terminateBtn = status === 'running'
|
||||||
? `<button type="button" class="btn-secondary btn-monitor-abort" onclick="cancelMCPToolExecution('${rawExecId.replace(/\\/g, '\\\\').replace(/'/g, "\\'")}')">${escapeHtml(terminateLabel)}</button>`
|
? `<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 `
|
return `
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<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>
|
||||||
<td>${toolName}</td>
|
<td>${toolName}</td>
|
||||||
<td><span class="${statusClass}">${escapeHtml(statusLabel)}</span></td>
|
<td><span class="${statusClass}">${escapeHtml(statusLabel)}</span></td>
|
||||||
@@ -5212,6 +5215,8 @@ async function deleteExecution(executionId) {
|
|||||||
throw new Error(error.error || deleteFailedMsg);
|
throw new Error(error.error || deleteFailedMsg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
monitorState.selectedExecutions.delete(executionId);
|
||||||
|
|
||||||
// 删除成功后刷新当前页面
|
// 删除成功后刷新当前页面
|
||||||
const currentPage = monitorState.pagination.page;
|
const currentPage = monitorState.pagination.page;
|
||||||
await refreshMonitorPanel(currentPage);
|
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() {
|
function updateBatchActionsState() {
|
||||||
const checkboxes = document.querySelectorAll('.monitor-execution-checkbox:checked');
|
const selectedCount = monitorState.selectedExecutions.size;
|
||||||
const selectedCount = checkboxes.length;
|
|
||||||
const batchActions = document.getElementById('monitor-batch-actions');
|
const batchActions = document.getElementById('monitor-batch-actions');
|
||||||
const selectedCountSpan = document.getElementById('monitor-selected-count');
|
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 + ' 项';
|
selectedCountSpan.textContent = typeof window.t === 'function' ? window.t('mcp.selectedCount', { count: selectedCount }) : '已选择 ' + selectedCount + ' 项';
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新全选复选框状态
|
// 更新全选复选框状态(仅反映当前页)
|
||||||
const selectAllCheckbox = document.getElementById('monitor-select-all');
|
const selectAllCheckbox = document.getElementById('monitor-select-all');
|
||||||
if (selectAllCheckbox) {
|
if (selectAllCheckbox) {
|
||||||
const allCheckboxes = document.querySelectorAll('.monitor-execution-checkbox');
|
const allCheckboxes = document.querySelectorAll('.monitor-execution-checkbox');
|
||||||
const allChecked = allCheckboxes.length > 0 && Array.from(allCheckboxes).every(cb => cb.checked);
|
if (allCheckboxes.length === 0) {
|
||||||
selectAllCheckbox.checked = allChecked;
|
selectAllCheckbox.checked = false;
|
||||||
selectAllCheckbox.indeterminate = selectedCount > 0 && selectedCount < allCheckboxes.length;
|
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');
|
const checkboxes = document.querySelectorAll('.monitor-execution-checkbox');
|
||||||
checkboxes.forEach(cb => {
|
checkboxes.forEach(cb => {
|
||||||
cb.checked = checkbox.checked;
|
cb.checked = checkbox.checked;
|
||||||
|
if (checkbox.checked) {
|
||||||
|
monitorState.selectedExecutions.add(cb.value);
|
||||||
|
} else {
|
||||||
|
monitorState.selectedExecutions.delete(cb.value);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
updateBatchActionsState();
|
updateBatchActionsState();
|
||||||
}
|
}
|
||||||
@@ -5269,6 +5296,7 @@ function selectAllExecutions() {
|
|||||||
const checkboxes = document.querySelectorAll('.monitor-execution-checkbox');
|
const checkboxes = document.querySelectorAll('.monitor-execution-checkbox');
|
||||||
checkboxes.forEach(cb => {
|
checkboxes.forEach(cb => {
|
||||||
cb.checked = true;
|
cb.checked = true;
|
||||||
|
monitorState.selectedExecutions.add(cb.value);
|
||||||
});
|
});
|
||||||
const selectAllCheckbox = document.getElementById('monitor-select-all');
|
const selectAllCheckbox = document.getElementById('monitor-select-all');
|
||||||
if (selectAllCheckbox) {
|
if (selectAllCheckbox) {
|
||||||
@@ -5284,6 +5312,7 @@ function deselectAllExecutions() {
|
|||||||
checkboxes.forEach(cb => {
|
checkboxes.forEach(cb => {
|
||||||
cb.checked = false;
|
cb.checked = false;
|
||||||
});
|
});
|
||||||
|
monitorState.selectedExecutions.clear();
|
||||||
const selectAllCheckbox = document.getElementById('monitor-select-all');
|
const selectAllCheckbox = document.getElementById('monitor-select-all');
|
||||||
if (selectAllCheckbox) {
|
if (selectAllCheckbox) {
|
||||||
selectAllCheckbox.checked = false;
|
selectAllCheckbox.checked = false;
|
||||||
@@ -5294,14 +5323,12 @@ function deselectAllExecutions() {
|
|||||||
|
|
||||||
// 批量删除执行记录
|
// 批量删除执行记录
|
||||||
async function batchDeleteExecutions() {
|
async function batchDeleteExecutions() {
|
||||||
const checkboxes = document.querySelectorAll('.monitor-execution-checkbox:checked');
|
const ids = Array.from(monitorState.selectedExecutions);
|
||||||
if (checkboxes.length === 0) {
|
if (ids.length === 0) {
|
||||||
const selectFirstMsg = typeof window.t === 'function' ? window.t('mcpMonitor.selectExecFirst') : '请先选择要删除的执行记录';
|
const selectFirstMsg = typeof window.t === 'function' ? window.t('mcpMonitor.selectExecFirst') : '请先选择要删除的执行记录';
|
||||||
alert(selectFirstMsg);
|
alert(selectFirstMsg);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ids = Array.from(checkboxes).map(cb => cb.value);
|
|
||||||
const count = ids.length;
|
const count = ids.length;
|
||||||
const batchConfirmMsg = typeof window.t === 'function' ? window.t('mcpMonitor.batchDeleteConfirm', { count: count }) : `确定要删除选中的 ${count} 条执行记录吗?此操作不可恢复。`;
|
const batchConfirmMsg = typeof window.t === 'function' ? window.t('mcpMonitor.batchDeleteConfirm', { count: count }) : `确定要删除选中的 ${count} 条执行记录吗?此操作不可恢复。`;
|
||||||
if (!confirm(batchConfirmMsg)) {
|
if (!confirm(batchConfirmMsg)) {
|
||||||
@@ -5326,6 +5353,10 @@ async function batchDeleteExecutions() {
|
|||||||
const result = await response.json().catch(() => ({}));
|
const result = await response.json().catch(() => ({}));
|
||||||
const deletedCount = result.deleted || count;
|
const deletedCount = result.deleted || count;
|
||||||
|
|
||||||
|
ids.forEach(function (id) {
|
||||||
|
monitorState.selectedExecutions.delete(id);
|
||||||
|
});
|
||||||
|
|
||||||
// 删除成功后刷新当前页面
|
// 删除成功后刷新当前页面
|
||||||
const currentPage = monitorState.pagination.page;
|
const currentPage = monitorState.pagination.page;
|
||||||
await refreshMonitorPanel(currentPage);
|
await refreshMonitorPanel(currentPage);
|
||||||
|
|||||||
@@ -293,6 +293,9 @@ async function ensureProjectsLoaded(force) {
|
|||||||
projectsCacheAll = list;
|
projectsCacheAll = list;
|
||||||
rebuildProjectNameMap(projectsCacheAll);
|
rebuildProjectNameMap(projectsCacheAll);
|
||||||
_projectsListReady = true;
|
_projectsListReady = true;
|
||||||
|
if (typeof window.refreshConversationProjectFilter === 'function') {
|
||||||
|
window.refreshConversationProjectFilter();
|
||||||
|
}
|
||||||
return projectsCacheAll;
|
return projectsCacheAll;
|
||||||
})
|
})
|
||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
@@ -371,6 +374,9 @@ async function loadProjectsList() {
|
|||||||
if (typeof refreshVulnerabilityProjectFilter === 'function') {
|
if (typeof refreshVulnerabilityProjectFilter === 'function') {
|
||||||
refreshVulnerabilityProjectFilter();
|
refreshVulnerabilityProjectFilter();
|
||||||
}
|
}
|
||||||
|
if (typeof window.refreshAllProjectFilterSelects === 'function') {
|
||||||
|
await window.refreshAllProjectFilterSelects();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function projectInitial(name) {
|
function projectInitial(name) {
|
||||||
@@ -2198,6 +2204,9 @@ async function applyChatProjectSelection(projectId) {
|
|||||||
setActiveProjectId(projectId);
|
setActiveProjectId(projectId);
|
||||||
}
|
}
|
||||||
updateChatProjectButtonLabel();
|
updateChatProjectButtonLabel();
|
||||||
|
if (typeof window.onConversationProjectBindingChanged === 'function') {
|
||||||
|
window.onConversationProjectBindingChanged(projectId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 对话页项目选择器:同步按钮文案;若浮层已打开则刷新列表 */
|
/** 对话页项目选择器:同步按钮文案;若浮层已打开则刷新列表 */
|
||||||
@@ -2326,3 +2335,4 @@ window.focusProjectFactGraphEdge = focusProjectFactGraphEdge;
|
|||||||
window.toggleProjectFactGraphConnectMode = toggleProjectFactGraphConnectMode;
|
window.toggleProjectFactGraphConnectMode = toggleProjectFactGraphConnectMode;
|
||||||
window.rebuildProjectNameMap = rebuildProjectNameMap;
|
window.rebuildProjectNameMap = rebuildProjectNameMap;
|
||||||
window.projectNameById = projectNameById;
|
window.projectNameById = projectNameById;
|
||||||
|
window.ensureProjectsLoaded = ensureProjectsLoaded;
|
||||||
|
|||||||
@@ -778,7 +778,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="sidebar-content">
|
<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="搜索历史记录..."
|
<input type="text" id="conversation-search-input" data-i18n="chat.searchHistory" data-i18n-attr="placeholder" placeholder="搜索历史记录..."
|
||||||
oninput="handleConversationSearch(this.value)"
|
oninput="handleConversationSearch(this.value)"
|
||||||
onkeypress="if(event.key === 'Enter') handleConversationSearch(this.value)" />
|
onkeypress="if(event.key === 'Enter') handleConversationSearch(this.value)" />
|
||||||
@@ -791,6 +791,15 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</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">
|
<div class="conversation-groups-section">
|
||||||
<div class="section-header">
|
<div class="section-header">
|
||||||
@@ -3764,6 +3773,10 @@
|
|||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<h2 id="batch-manage-title">管理对话记录·共<span id="batch-manage-count">0</span>条</h2>
|
<h2 id="batch-manage-title">管理对话记录·共<span id="batch-manage-count">0</span>条</h2>
|
||||||
<div class="batch-manage-header-actions">
|
<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">
|
<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)" />
|
<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">
|
<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="全选" />
|
<input type="checkbox" id="batch-select-all" onchange="toggleSelectAllBatch()" data-i18n="batchManageModal.selectAll" data-i18n-attr="title" title="全选" />
|
||||||
</div>
|
</div>
|
||||||
<div class="batch-table-col-name" data-i18n="batchManageModal.conversationName">对话名称</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-time" data-i18n="batchManageModal.lastTime">最近一次对话时间</div>
|
||||||
<div class="batch-table-col-action" data-i18n="batchManageModal.action">操作</div>
|
<div class="batch-table-col-action" data-i18n="batchManageModal.action">操作</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user