// AI API统一转换代理系统前端交互逻辑 class APIConverter { constructor() { this.currentTaskId = null; this.progressInterval = null; this.channels = []; this.init(); } init() { this.setupEventListeners(); this.setupTabNavigation(); this.loadProviders(); this.loadCapabilities(); this.loadChannels(); this.setupModernAnimations(); } setupEventListeners() { // 渠道表单提交事件 if (document.getElementById('channelForm')) { document.getElementById('channelForm').addEventListener('submit', (e) => { e.preventDefault(); const form = e.target; if (form.dataset.isEditing === 'true') { // 编辑模式 const channelId = form.dataset.editingChannelId; this.updateChannel(channelId); } else { // 创建模式 this.createChannel(); } }); } // 检测表单提交事件 if (document.getElementById('detectionForm')) { document.getElementById('detectionForm').addEventListener('submit', (e) => { e.preventDefault(); this.startDetection(); }); } // 提供商选择事件 if (document.getElementById('provider')) { document.getElementById('provider').addEventListener('change', (e) => { this.updateDefaultUrl(e.target.value); this.updateModelOptions(e.target.value); }); } // 渠道提供商选择事件 if (document.getElementById('channel_provider')) { document.getElementById('channel_provider').addEventListener('change', (e) => { this.updateChannelDefaultUrl(e.target.value); }); } } updateDefaultUrl(provider) { const defaultUrls = { 'openai': 'https://api.openai.com/v1', 'anthropic': 'https://api.anthropic.com', 'gemini': 'https://generativelanguage.googleapis.com/v1beta' }; const baseUrlInput = document.getElementById('detection_base_url'); if (defaultUrls[provider] && baseUrlInput) { baseUrlInput.value = defaultUrls[provider]; } } updateChannelDefaultUrl(provider) { const defaultUrls = { 'openai': 'https://api.openai.com/v1', 'anthropic': 'https://api.anthropic.com', 'gemini': 'https://generativelanguage.googleapis.com/v1beta' }; const baseUrlInput = document.getElementById('base_url'); if (defaultUrls[provider] && baseUrlInput) { baseUrlInput.value = defaultUrls[provider]; } } updateModelOptions(provider) { const modelSelect = document.getElementById('target_model_select'); if (!modelSelect) return; const commonModels = { 'openai': [ 'gpt-4o', 'gpt-4o-mini', 'gpt-4-turbo', 'gpt-4', 'gpt-3.5-turbo' ], 'anthropic': [ 'claude-3-5-sonnet-20241022', 'claude-3-5-haiku-20241022', 'claude-3-opus-20240229', 'claude-3-sonnet-20240229', 'claude-3-haiku-20240307' ], 'gemini': [ 'gemini-1.5-pro', 'gemini-1.5-flash', 'gemini-1.0-pro' ] }; // 清空现有选项 modelSelect.innerHTML = ''; // 添加对应的模型选项 if (commonModels[provider]) { commonModels[provider].forEach(model => { const option = document.createElement('option'); option.value = model; option.textContent = model; modelSelect.appendChild(option); }); } } setupTabNavigation() { // 标签页切换事件 const tabButtons = document.querySelectorAll('.tab-button'); tabButtons.forEach(button => { button.addEventListener('click', (e) => { const targetTab = e.target.getAttribute('data-tab'); this.switchTab(targetTab); }); }); } switchTab(tabName) { // 移除所有活动状态 document.querySelectorAll('.tab-button').forEach(btn => { btn.classList.remove('active'); }); document.querySelectorAll('.tab-content').forEach(content => { content.classList.remove('active'); }); // 激活选中的标签页 const activeButton = document.querySelector(`[data-tab="${tabName}"]`); const activeContent = document.getElementById(`${tabName}-tab`); if (activeButton && activeContent) { activeButton.classList.add('active'); activeContent.classList.add('active'); } } async loadProviders() { try { const response = await fetch('/api/providers'); const data = await response.json(); const channelProviderSelect = document.getElementById('channel_provider'); if (channelProviderSelect) { data.providers.forEach(provider => { const option = document.createElement('option'); option.value = provider.id; option.textContent = provider.name; channelProviderSelect.appendChild(option); }); } } catch (error) { console.error('Failed to load providers:', error); } } async loadCapabilities() { try { const response = await fetch('/api/capabilities'); await response.json(); // 处理能力选项 } catch (error) { console.error('Failed to load capabilities:', error); } } async startDetection() { // 检测逻辑 console.log('Starting detection...'); } setupModernAnimations() { // 动画设置 const inputs = document.querySelectorAll('input, select, textarea'); inputs.forEach(input => { input.addEventListener('focus', () => { input.closest('.form-group').classList.add('focused'); }); input.addEventListener('blur', () => { input.closest('.form-group').classList.remove('focused'); }); }); } // 渠道管理方法 async createChannel() { const form = document.getElementById('channelForm'); const formData = new FormData(form); const channelData = Object.fromEntries(formData.entries()); // 转换数值类型 channelData.timeout = parseInt(channelData.timeout); channelData.max_retries = parseInt(channelData.max_retries); try { const sessionToken = localStorage.getItem('session_token'); const response = await fetch('/api/channels', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${sessionToken}` }, body: JSON.stringify(channelData) }); const result = await response.json(); if (response.ok) { alert('渠道创建成功!'); form.reset(); await this.loadChannels(); // 重新加载渠道列表 } else { alert('创建失败: ' + (result.detail || '未知错误')); } } catch (error) { alert('请求失败: ' + error.message); } } async loadChannels() { console.log('开始加载渠道列表...'); try { const sessionToken = localStorage.getItem('session_token'); const response = await fetch('/api/channels', { headers: { 'Authorization': `Bearer ${sessionToken}` } }); const result = await response.json(); console.log('API响应:', result); if (response.ok) { this.channels = result.channels; console.log('渠道数量:', this.channels.length); this.updateChannelsList(); } else { console.error('加载渠道列表失败:', result.detail); // 显示错误信息 const channelsList = document.getElementById('channelsList'); if (channelsList) { channelsList.innerHTML = '

加载渠道列表失败

'; } } } catch (error) { console.error('加载渠道列表失败:', error); // 显示错误信息 const channelsList = document.getElementById('channelsList'); if (channelsList) { channelsList.innerHTML = '

加载渠道列表失败

'; } } } updateChannelsList() { console.log('更新渠道列表,渠道数量:', this.channels.length); const channelsList = document.getElementById('channelsList'); if (!channelsList) { console.error('找不到channelsList元素'); return; } if (this.channels.length === 0) { console.log('渠道列表为空,显示暂无配置信息'); channelsList.innerHTML = '

暂无配置的渠道

'; return; } const channelsHTML = this.channels.map(channel => `

${channel.name}

提供商: ${channel.provider}

URL: ${channel.base_url}

自定义Key: ${channel.custom_key}

状态: ${channel.enabled ? '启用' : '禁用'}

创建时间: ${new Date(channel.created_at).toLocaleString()}

`).join(''); channelsList.innerHTML = channelsHTML; } async testChannel(channelId) { try { const response = await fetch(`/api/channels/${channelId}/test`); const result = await response.json(); if (response.ok) { alert(`测试成功!响应时间: ${result.test_result.response_time}ms`); } else { alert('测试失败: ' + (result.detail || '未知错误')); } } catch (error) { alert('测试失败: ' + error.message); } } async toggleChannel(channelId) { const channel = this.channels.find(c => c.id === channelId); if (!channel) return; try { const response = await fetch(`/api/channels/${channelId}`, { method: 'PUT', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ enabled: !channel.enabled }) }); const result = await response.json(); if (response.ok) { this.loadChannels(); // 重新加载渠道列表 } else { alert('操作失败: ' + (result.detail || '未知错误')); } } catch (error) { alert('操作失败: ' + error.message); } } editChannel(channelId) { const channel = this.channels.find(c => c.id === channelId); if (!channel) { alert('渠道不存在'); return; } // 修改表单标题和按钮 document.querySelector('.channel-form h3').textContent = '编辑渠道'; const submitButton = document.querySelector('#channelForm button[type="submit"]'); submitButton.textContent = '更新渠道'; // 存储当前编辑的渠道ID到表单数据属性 const form = document.getElementById('channelForm'); form.dataset.editingChannelId = channelId; form.dataset.isEditing = 'true'; // 填充表单数据 document.getElementById('name').value = channel.name || ''; document.getElementById('channel_provider').value = channel.provider || ''; document.getElementById('base_url').value = channel.base_url || ''; // 对于API密钥,如果已掩码则设置placeholder,否则清空 const apiKeyInput = document.getElementById('api_key'); if (channel.api_key && channel.api_key.includes('***')) { apiKeyInput.value = ''; apiKeyInput.placeholder = channel.api_key + ' (留空保持不变)'; apiKeyInput.required = false; // 编辑时可以不填 } else { apiKeyInput.value = ''; apiKeyInput.placeholder = '输入新的API密钥或留空保持不变'; apiKeyInput.required = false; // 编辑时可以不填 } document.getElementById('custom_key').value = channel.custom_key || ''; document.getElementById('timeout').value = channel.timeout || 30; document.getElementById('max_retries').value = channel.max_retries || 3; // 添加取消按钮 if (!document.getElementById('cancelEdit')) { const cancelButton = document.createElement('button'); cancelButton.id = 'cancelEdit'; cancelButton.type = 'button'; cancelButton.className = 'btn-secondary'; cancelButton.textContent = '取消编辑'; cancelButton.onclick = () => this.cancelEdit(); submitButton.parentNode.insertBefore(cancelButton, submitButton.nextSibling); } // 添加编辑状态样式提示 const channelForm = document.querySelector('.channel-form'); if (channelForm) { channelForm.classList.add('editing-mode'); // 延迟滚动,确保DOM更新完成 setTimeout(() => { channelForm.scrollIntoView({ behavior: 'smooth', block: 'start', inline: 'nearest' }); }, 100); } } cancelEdit() { // 恢复表单标题和按钮 document.querySelector('.channel-form h3').textContent = '添加新渠道'; const submitButton = document.querySelector('#channelForm button[type="submit"]'); submitButton.textContent = '添加渠道'; // 清除编辑状态 const form = document.getElementById('channelForm'); delete form.dataset.editingChannelId; delete form.dataset.isEditing; // 重置表单 form.reset(); // 重置API密钥字段的placeholder和必填属性 const apiKeyInput = document.getElementById('api_key'); apiKeyInput.placeholder = ''; apiKeyInput.required = true; // 移除取消按钮 const cancelButton = document.getElementById('cancelEdit'); if (cancelButton) { cancelButton.remove(); } // 移除编辑状态样式 const channelForm = document.querySelector('.channel-form'); if (channelForm) { channelForm.classList.remove('editing-mode'); } } async updateChannel(channelId) { const form = document.getElementById('channelForm'); const formData = new FormData(form); const channelData = Object.fromEntries(formData.entries()); // 转换数值类型 channelData.timeout = parseInt(channelData.timeout); channelData.max_retries = parseInt(channelData.max_retries); // 输入验证 if (!channelData.name || !channelData.provider || !channelData.base_url || !channelData.custom_key) { alert('请填写所有必填字段'); return; } // 如果API密钥是掩码形式或为空,则不更新密钥 if (!channelData.api_key || channelData.api_key.trim() === '' || channelData.api_key.includes('***')) { delete channelData.api_key; } try { const response = await fetch(`/api/channels/${channelId}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(channelData) }); const result = await response.json(); if (response.ok) { alert('渠道更新成功!'); this.cancelEdit(); await this.loadChannels(); // 重新加载渠道列表 } else { if (response.status === 404) { alert('渠道不存在,可能已被删除。页面将刷新以同步最新数据。'); await this.loadChannels(); this.cancelEdit(); } else { alert('更新失败: ' + (result.detail || '未知错误')); } } } catch (error) { alert('请求失败: ' + error.message); } } async deleteChannel(channelId) { if (!confirm('确定要删除这个渠道吗?')) return; try { const response = await fetch(`/api/channels/${channelId}`, { method: 'DELETE', headers: { 'Content-Type': 'application/json' } }); const result = await response.json(); if (response.ok) { alert('删除成功!'); await this.loadChannels(); // 重新加载渠道列表 } else { alert('删除失败: ' + (result.detail || '未知错误')); } } catch (error) { alert('删除失败: ' + error.message); } } } // 现代化的平滑滚动效果 class SmoothScroll { constructor() { this.init(); } init() { // 为链接添加平滑滚动 document.querySelectorAll('a[href^="#"]').forEach(anchor => { anchor.addEventListener('click', (e) => { e.preventDefault(); const target = document.querySelector(anchor.getAttribute('href')); if (target) { target.scrollIntoView({ behavior: 'smooth', block: 'start' }); } }); }); } } // 全局函数定义 function updateTargetModel() { const select = document.getElementById('target_model_select'); const input = document.getElementById('target_model'); if (select && input && select.value) { input.value = select.value; } } function selectAllCapabilities() { document.querySelectorAll('input[name="capabilities"]').forEach(cb => { cb.checked = true; }); } function clearAllCapabilities() { document.querySelectorAll('input[name="capabilities"]').forEach(cb => { cb.checked = false; }); } async function fetchModels() { const provider = document.getElementById('provider').value; const baseUrl = document.getElementById('detection_base_url').value; const apiKey = document.getElementById('detection_api_key').value; if (!provider || !baseUrl || !apiKey) { alert('请先填写提供商、API基础URL和API密钥'); return; } const btn = document.getElementById('fetch_models_btn'); btn.disabled = true; btn.textContent = '获取中...'; try { const response = await fetch('/api/fetch_models', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ provider: provider, base_url: baseUrl, api_key: apiKey }) }); const data = await response.json(); if (response.ok) { const select = document.getElementById('target_model_select'); select.innerHTML = ''; data.models.forEach(model => { const option = document.createElement('option'); option.value = model; option.textContent = model; select.appendChild(option); }); } else { alert('获取模型列表失败: ' + (data.detail || '未知错误')); } } catch (error) { alert('请求失败: ' + error.message); } finally { btn.disabled = false; btn.textContent = '获取模型列表'; } } // 全局实例 let apiConverter; // 初始化应用函数 function initializeApp() { apiConverter = new APIConverter(); new SmoothScroll(); // 添加键盘导航支持 document.addEventListener('keydown', (e) => { if (e.key === 'Tab') { document.body.classList.add('keyboard-navigation'); } }); document.addEventListener('mousedown', () => { document.body.classList.remove('keyboard-navigation'); }); } // 如果页面已经加载完成,直接初始化;否则等待DOMContentLoaded if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', initializeApp); } else { initializeApp(); }