showToolSelectionDialog()

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);
        };
    }