From ba5d49005bfe18658c7bca07da66bc4595e3717c Mon Sep 17 00:00:00 2001 From: hubian <908234780@qq.com> Date: Mon, 27 Apr 2026 23:44:37 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E6=9B=B4=E5=A4=9A?= =?UTF-8?q?=E5=B7=A5=E5=85=B7=E6=8C=89=E9=92=AE=EF=BC=8C=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E5=A4=9A=E9=80=89=E5=B7=A5=E5=85=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/app.py | 5 ++ www/app.js | 163 ++++++++++++++++++++++++++++++++++++++++++ www/style.css | 187 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 355 insertions(+) diff --git a/backend/app.py b/backend/app.py index c6af9ca..08bf12f 100644 --- a/backend/app.py +++ b/backend/app.py @@ -1332,6 +1332,10 @@ def get_frontend_config(): cursor.execute('SELECT * FROM tool_configs WHERE is_default=1 AND is_active=1') tools = [dict(row) for row in cursor.fetchall()] + # 获取所有活跃的工具配置(供前端选择) + cursor.execute('SELECT tool_id, name, type, provider, is_active FROM tool_configs WHERE is_active=1') + allTools = [dict(row) for row in cursor.fetchall()] + # 获取所有智能体(上线且活跃) cursor.execute('SELECT agent_id, name, avatar, category, description, system_prompt, heat, tags, enable_tools FROM agents WHERE is_online=1 AND is_active=1') agents = [dict(row) for row in cursor.fetchall()] @@ -1349,6 +1353,7 @@ def get_frontend_config(): config = { 'llm': dict(llm) if llm else None, 'tools': tools, + 'allTools': allTools, # 所有活跃的工具(供前端选择) 'agents': agents, 'chat_config': dict(chat_config) if chat_config else None, 'system': { diff --git a/www/app.js b/www/app.js index 6b7cce0..16b1e50 100644 --- a/www/app.js +++ b/www/app.js @@ -116,6 +116,16 @@ async function loadBackendConfig() { }; } + // 加载工具列表 + if (backendConfig.allTools) { + allTools = backendConfig.allTools; + } + + // 加载默认启用的工具 + if (backendConfig.tools) { + enabledTools = backendConfig.tools.map(t => t.tool_id); + } + updateAgentsDisplay(); console.log('后台配置已加载', backendConfig); } catch (e) { @@ -282,6 +292,8 @@ function getAgentConversationHistory(limit = 5) { let enableThinking = false; // 深度思考 let enableSearch = false; // 联网搜索 let autoScrollEnabled = true; // 自动滚动(用户滚动后可关闭) +let enabledTools = []; // 启用的工具列表(多选) +let allTools = []; // 所有可用的工具列表 // DOM 元素(初始为 null,在 openConversation 时重新获取) let appContainer = null; @@ -3129,6 +3141,11 @@ function showAgentChatPage() { 联网搜索 +
+
+
+
+

勾选要启用的工具(多选)

+
+ ${toolsHtml} +
+
+ + + + `; + + // 添加到页面 + const existingPopup = document.getElementById('toolsPopup'); + if (existingPopup) existingPopup.remove(); + + document.body.appendChild(document.createElement('div')); + document.body.lastElementChild.outerHTML = popupHtml; + + // 绑定事件 + document.querySelectorAll('.tool-option').forEach(option => { + option.addEventListener('click', () => { + option.classList.toggle('selected'); + // 更新复选框图标 + const checkbox = option.querySelector('.tool-checkbox svg'); + const isSelected = option.classList.contains('selected'); + checkbox.innerHTML = isSelected + ? '' + : ''; + }); + }); + + // 关闭按钮 + document.getElementById('toolsPopupClose').addEventListener('click', closeToolsPopup); + document.getElementById('toolsCancelBtn').addEventListener('click', closeToolsPopup); + document.getElementById('toolsConfirmBtn').addEventListener('click', confirmToolsSelection); + + // 点击背景关闭 + document.getElementById('toolsPopup').addEventListener('click', (e) => { + if (e.target.id === 'toolsPopup') { + closeToolsPopup(); + } + }); +} + +function closeToolsPopup() { + const popup = document.getElementById('toolsPopup'); + if (popup) popup.remove(); +} + +function confirmToolsSelection() { + // 获取选中的工具 + const selectedTools = []; + document.querySelectorAll('.tool-option.selected').forEach(option => { + selectedTools.push(option.getAttribute('data-tool-id')); + }); + + enabledTools = selectedTools; + + // 更新按钮显示 + const moreToolsBtn = document.getElementById('moreToolsBtn'); + if (moreToolsBtn) { + const countSpan = moreToolsBtn.querySelector('.tools-count'); + if (enabledTools.length > 0) { + if (countSpan) { + countSpan.textContent = enabledTools.length; + } else { + const span = document.createElement('span'); + span.className = 'tools-count'; + span.textContent = enabledTools.length; + moreToolsBtn.appendChild(span); + } + } else if (countSpan) { + countSpan.remove(); + } + } + + closeToolsPopup(); + showToast(`已启用 ${enabledTools.length} 个工具`); +} + // 渲染头像(支持 emoji 和上传的图片) function renderAvatar(avatar) { if (!avatar) return '👤'; diff --git a/www/style.css b/www/style.css index 0483e7b..26f32a3 100644 --- a/www/style.css +++ b/www/style.css @@ -2740,6 +2740,193 @@ body { flex-shrink: 0; } +/* 更多工具按钮 */ +.tools-btn { + position: relative; +} + +.tools-count { + position: absolute; + top: -4px; + right: -4px; + min-width: 16px; + height: 16px; + padding: 0 4px; + background: #ef4444; + color: white; + font-size: 11px; + font-weight: 600; + border-radius: 8px; + display: flex; + align-items: center; + justify-content: center; +} + +/* 工具选择弹窗 */ +.tools-popup { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.5); + display: flex; + align-items: center; + justify-content: center; + z-index: 1000; +} + +.tools-popup-content { + width: 90%; + max-width: 400px; + background: white; + border-radius: 16px; + overflow: hidden; +} + +.tools-popup-header { + display: flex; + align-items: center; + justify-content: space-between; + padding: 16px 20px; + background: linear-gradient(135deg, var(--primary) 0%, #764ba2 100%); + color: white; +} + +.tools-popup-header h3 { + margin: 0; + font-size: 18px; +} + +.tools-popup-close { + width: 28px; + height: 28px; + display: flex; + align-items: center; + justify-content: center; + background: rgba(255, 255, 255, 0.2); + border: none; + border-radius: 14px; + color: white; + font-size: 20px; + cursor: pointer; + transition: background 0.2s; +} + +.tools-popup-close:hover { + background: rgba(255, 255, 255, 0.3); +} + +.tools-popup-body { + padding: 16px 20px; +} + +.tools-popup-tip { + color: #718096; + font-size: 13px; + margin-bottom: 16px; +} + +.tools-list { + display: flex; + flex-direction: column; + gap: 12px; + max-height: 300px; + overflow-y: auto; +} + +.tool-option { + display: flex; + align-items: center; + gap: 12px; + padding: 12px 16px; + background: #f5f7fa; + border: 2px solid transparent; + border-radius: 12px; + cursor: pointer; + transition: all 0.2s; +} + +.tool-option:hover { + background: #e8ecf1; +} + +.tool-option.selected { + background: rgba(102, 126, 234, 0.1); + border-color: var(--primary); +} + +.tool-checkbox { + flex-shrink: 0; + color: #718096; +} + +.tool-option.selected .tool-checkbox { + color: var(--primary); +} + +.tool-icon { + width: 40px; + height: 40px; + display: flex; + align-items: center; + justify-content: center; + background: white; + border-radius: 10px; + font-size: 20px; +} + +.tool-info { + flex: 1; +} + +.tool-name { + font-weight: 500; + color: #2d3748; +} + +.tool-type { + font-size: 12px; + color: #718096; + margin-top: 2px; +} + +.tools-popup-footer { + display: flex; + gap: 12px; + padding: 16px 20px; + background: #f5f7fa; +} + +.tools-popup-btn { + flex: 1; + padding: 12px; + border: none; + border-radius: 10px; + font-size: 14px; + font-weight: 500; + cursor: pointer; + transition: all 0.2s; +} + +.tools-popup-btn.cancel { + background: #e2e8f0; + color: #4a5568; +} + +.tools-popup-btn.cancel:hover { + background: #cbd5e0; +} + +.tools-popup-btn.confirm { + background: linear-gradient(135deg, var(--primary) 0%, #764ba2 100%); + color: white; +} + +.tools-popup-btn.confirm:hover { + transform: scale(1.02); +} + /* 导航按钮样式 */ .nav-btn { padding: 6px 8px;