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