in spring-ai-alibaba-jmanus/src/main/resources/static/admin/js/admin-ui.js [409:676]
showToolSelectionDialog(availableTools, currentTools, onSelect) {
// 创建遮罩层
const overlay = document.createElement('div');
overlay.className = 'dialog-overlay';
document.body.appendChild(overlay);
// 获取或创建对话框
let dialog = document.querySelector('.tool-selection-dialog');
if (!dialog) {
dialog = document.createElement('div');
dialog.className = 'tool-selection-dialog';
dialog.innerHTML = `
<div class="dialog-header">
<h3>选择工具</h3>
<input type="text" class="tool-search" placeholder="搜索工具...">
</div>
<div class="tool-list-container"></div>
<div class="dialog-footer">
<button class="cancel-btn">取消</button>
<button class="confirm-btn">确认</button>
</div>
`;
document.body.appendChild(dialog);
}
const toolListContainer = dialog.querySelector('.tool-list-container');
const searchInput = dialog.querySelector('.tool-search');
// 复制工具列表以便于排序和处理 (Deep copy to avoid modifying original objects)
const toolsCopy = JSON.parse(JSON.stringify(availableTools));
// 为每个工具添加isSelected属性,标记其是否已被当前Agent选择
toolsCopy.forEach(tool => {
tool.isSelected = currentTools.includes(tool.key);
});
// 初始化对话框本地的 selectedTools 数组
let selectedTools = toolsCopy.filter(tool => tool.isSelected);
// 按服务组对工具进行分组
const groupedTools = this.groupToolsByServiceGroup(toolsCopy);
// 渲染分组后的工具选择列表
this.renderToolSelectionList(toolListContainer, groupedTools);
// 排序功能
const handleSort = (e) => {
const sortMethod = e.target.value;
let sortedTools = [...toolsCopy];
switch(sortMethod) {
case 'name':
sortedTools.sort((a, b) => (a.name || a.key).localeCompare(b.name || b.key));
break;
case 'enabled':
sortedTools.sort((a, b) => (b.isSelected ? 1 : 0) - (a.isSelected ? 1 : 0));
break;
case 'group':
default:
// 默认按服务组排序,在 groupToolsByServiceGroup 中处理
break;
}
// 更新当前工具列表
const filteredGroupedTools = this.groupToolsByServiceGroup(sortedTools);
// 重新渲染工具列表
this.renderToolSelectionList(toolListContainer, filteredGroupedTools);
// 重新绑定事件(因为DOM已重新生成)
addToolListEventListeners();
};
// 搜索功能
const handleSearch = (e) => {
const searchText = e.target.value.toLowerCase();
const filteredTools = toolsCopy.filter(tool =>
tool.key.toLowerCase().includes(searchText) ||
(tool.name && tool.name.toLowerCase().includes(searchText)) ||
(tool.description && tool.description.toLowerCase().includes(searchText)) ||
(tool.serviceGroup && tool.serviceGroup.toLowerCase().includes(searchText))
);
// 重新分组过滤后的工具
const filteredGroupedTools = this.groupToolsByServiceGroup(filteredTools);
// 渲染过滤后的分组工具
this.renderToolSelectionList(toolListContainer, filteredGroupedTools);
// 重新绑定事件(因为DOM已重新生成)
addToolListEventListeners();
};
// 处理工具启用/禁用状态
const handleToolEnableToggle = (e) => {
if (!e.target.classList.contains('tool-enable-checkbox')) return;
const toolItem = e.target.closest('.tool-selection-item');
if (!toolItem) return;
const toolKey = toolItem.dataset.toolKey;
const toolInDialog = toolsCopy.find(t => t.key === toolKey);
if (toolInDialog) {
toolInDialog.isSelected = e.target.checked; // Update the master copy for the dialog
if (e.target.checked) {
// If checked, ensure it's in the dialog's selectedTools array
if (!selectedTools.some(st => st.key === toolKey)) {
selectedTools.push(toolInDialog);
}
} else {
// If unchecked, remove it from the dialog's selectedTools array
const index = selectedTools.findIndex(st => st.key === toolKey);
if (index !== -1) {
selectedTools.splice(index, 1);
}
}
}
// Update group counts display if necessary (can be done by re-rendering or specific update)
// For now, focusing on the core logic of selectedTools
};
// 处理组级别启用/禁用
const handleGroupEnableToggle = (e) => {
if (!e.target.classList.contains('group-enable-checkbox')) return;
const groupHeader = e.target.closest('.tool-group-header');
if (!groupHeader) return;
const groupName = groupHeader.dataset.group;
const isEnabled = e.target.checked;
toolsCopy.forEach(toolInDialog => {
if ((toolInDialog.serviceGroup || '未分组') === groupName) {
toolInDialog.isSelected = isEnabled;
if (isEnabled) {
// If enabling group, ensure tool is in dialog's selectedTools
if (!selectedTools.some(st => st.key === toolInDialog.key)) {
selectedTools.push(toolInDialog);
}
} else {
// If disabling group, remove tool from dialog's selectedTools
const index = selectedTools.findIndex(st => st.key === toolInDialog.key);
if (index !== -1) {
selectedTools.splice(index, 1);
}
}
}
});
// Update UI of individual checkboxes within this group in the dialog
const groupContent = groupHeader.nextElementSibling;
if (groupContent) {
const checkboxes = groupContent.querySelectorAll('.tool-selection-item .tool-enable-checkbox');
checkboxes.forEach(checkbox => {
const toolItemCheckbox = checkbox.closest('.tool-selection-item');
if (toolItemCheckbox) {
const toolKey = toolItemCheckbox.dataset.toolKey;
const toolInGroup = toolsCopy.find(t => t.key === toolKey && (t.serviceGroup || '未分组') === groupName);
if (toolInGroup) {
checkbox.checked = toolInGroup.isSelected;
}
}
});
}
// Update group counts display if necessary
};
// 显示对话框和遮罩
overlay.style.display = 'block';
dialog.style.display = 'block';
setTimeout(() => overlay.classList.add('show'), 10);
// 处理工具选择
const handleToolSelection = (e) => {
// 忽略启用/禁用复选框点击导致的事件冒泡
if (e.target.classList.contains('tool-enable-checkbox')) return;
// 处理单个工具选择
const toolItem = e.target.closest('.tool-selection-item');
if (toolItem) {
const toolKey = toolItem.dataset.toolKey;
// 添加到已选工具列表(不再切换selected类)
const tool = toolsCopy.find(t => t.key === toolKey);
if (tool && !selectedTools.some(t => t.key === toolKey)) {
selectedTools.push(tool);
}
}
};
// 选择全部功能已移除
// 为工具列表添加事件监听器
const addToolListEventListeners = () => {
// 绑定排序事件
const sortSelect = toolListContainer.querySelector('.tool-sort-select');
if (sortSelect) {
sortSelect.addEventListener('change', handleSort);
}
// 绑定工具选择事件
const toolItems = toolListContainer.querySelectorAll('.tool-selection-item');
toolItems.forEach(item => {
item.addEventListener('click', handleToolSelection);
});
// 绑定工具启用状态切换事件
const toolEnableCheckboxes = toolListContainer.querySelectorAll('.tool-enable-checkbox');
toolEnableCheckboxes.forEach(checkbox => {
checkbox.addEventListener('change', handleToolEnableToggle);
});
// 绑定组切换事件(已移除)
// 绑定组启用状态切换事件
const groupEnableCheckboxes = toolListContainer.querySelectorAll('.group-enable-checkbox');
groupEnableCheckboxes.forEach(checkbox => {
checkbox.addEventListener('change', handleGroupEnableToggle);
});
};
// 初始绑定事件
searchInput.addEventListener('input', handleSearch);
addToolListEventListeners();
// 确认按钮
const handleConfirm = () => {
// 直接将 selectedTools 数组传递给 onSelect 回调
// 即使 selectedTools 为空数组,回调函数也应该能正确处理
onSelect(selectedTools);
closeDialog();
};
// 取消按钮
const handleCancel = () => {
closeDialog();
};
// ESC键关闭
const handleKeyDown = (e) => {
if (e.key === 'Escape') {
closeDialog();
}
};
dialog.querySelector('.confirm-btn').addEventListener('click', handleConfirm);
dialog.querySelector('.cancel-btn').addEventListener('click', handleCancel);
document.addEventListener('keydown', handleKeyDown);
// 关闭对话框并清理事件监听器
const closeDialog = () => {
overlay.classList.remove('show');
setTimeout(() => {
dialog.style.display = 'none';
overlay.style.display = 'none';
overlay.remove();
// 清理事件监听器
searchInput.removeEventListener('input', handleSearch);
dialog.querySelector('.confirm-btn').removeEventListener('click', handleConfirm);
dialog.querySelector('.cancel-btn').removeEventListener('click', handleCancel);
document.removeEventListener('keydown', handleKeyDown);
}, 300);
};
}