feat: API配置前端持久化保存

This commit is contained in:
Cassianvale
2025-03-04 16:09:12 +08:00
parent e28b1342c3
commit e9ba47c35f
2 changed files with 128 additions and 48 deletions

View File

@@ -71,13 +71,19 @@
placeholder="输入您的API Key"> placeholder="输入您的API Key">
<p class="mt-1 text-sm text-gray-500">如不填写,将使用系统默认配置</p> <p class="mt-1 text-sm text-gray-500">如不填写,将使用系统默认配置</p>
</div> </div>
<div class="flex justify-end"> <div class="flex justify-between">
<button id="resetApiConfig" class="text-gray-600 hover:text-gray-800 text-sm mr-3"> <div class="flex items-center">
重置为默认 <input type="checkbox" id="saveApiConfig" class="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded">
</button> <label for="saveApiConfig" class="ml-2 block text-sm text-gray-700">保存配置到本地</label>
<button id="testApiConfig" class="bg-blue-100 text-blue-700 px-3 py-1 rounded hover:bg-blue-200 text-sm"> </div>
测试连接 <div>
</button> <button id="resetApiConfig" class="text-gray-600 hover:text-gray-800 text-sm mr-3">
重置为默认
</button>
<button id="testApiConfig" class="bg-blue-100 text-blue-700 px-3 py-1 rounded hover:bg-blue-200 text-sm">
测试连接
</button>
</div>
</div> </div>
</div> </div>
</div> </div>
@@ -430,7 +436,7 @@
} catch (error) { } catch (error) {
console.error('请求失败:', error); console.error('请求失败:', error);
resultContent.innerHTML = ` resultContent.innerHTML = `
<div class="p-4 bg-red-50 text-red-600 rounded"> <div class="p-6 bg-yellow-50 text-yellow-600 rounded-lg text-center">
分析出错:${error.message} 分析出错:${error.message}
</div> </div>
`; `;
@@ -705,73 +711,147 @@
<script> <script>
document.addEventListener('DOMContentLoaded', function() { document.addEventListener('DOMContentLoaded', function() {
// API配置面板切换 // API配置面板切换
const toggleBtn = document.getElementById('toggleApiConfig'); const toggleApiConfig = document.getElementById('toggleApiConfig');
const configPanel = document.getElementById('apiConfigPanel'); const apiConfigPanel = document.getElementById('apiConfigPanel');
const toggleText = document.getElementById('toggleApiConfigText'); const toggleApiConfigText = document.getElementById('toggleApiConfigText');
const toggleIcon = document.getElementById('toggleApiConfigIcon'); const toggleApiConfigIcon = document.getElementById('toggleApiConfigIcon');
const apiUrl = document.getElementById('apiUrl');
const apiKey = document.getElementById('apiKey');
const apiModel = document.getElementById('apiModel');
const saveApiConfig = document.getElementById('saveApiConfig');
const resetApiConfig = document.getElementById('resetApiConfig');
const testApiConfig = document.getElementById('testApiConfig');
toggleBtn.addEventListener('click', function() { // 从localStorage加载保存的配置
const isHidden = configPanel.classList.contains('hidden'); loadApiConfig();
configPanel.classList.toggle('hidden', !isHidden);
toggleText.textContent = isHidden ? '隐藏配置' : '显示配置'; // 切换API配置面板显示/隐藏
toggleIcon.style.transform = isHidden ? 'rotate(180deg)' : ''; toggleApiConfig.addEventListener('click', function() {
apiConfigPanel.classList.toggle('hidden');
if (apiConfigPanel.classList.contains('hidden')) {
toggleApiConfigText.textContent = '显示配置';
toggleApiConfigIcon.innerHTML = '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path>';
} else {
toggleApiConfigText.textContent = '隐藏配置';
toggleApiConfigIcon.innerHTML = '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 15l7-7 7 7"></path>';
}
}); });
// 重置API配置 // 重置API配置
document.getElementById('resetApiConfig').addEventListener('click', function() { resetApiConfig.addEventListener('click', function() {
document.getElementById('apiUrl').value = '{{ default_api_url }}'; apiUrl.value = '{{ default_api_url }}';
document.getElementById('apiModel').value = '{{ default_api_model }}'; apiKey.value = '';
document.getElementById('apiKey').value = ''; apiModel.value = '{{ default_api_model }}';
saveApiConfig.checked = false;
// 清除localStorage中的配置
localStorage.removeItem('apiConfig');
alert('已重置为默认配置');
}); });
// 测试API连接 // 测试API连接
document.getElementById('testApiConfig').addEventListener('click', async function() { testApiConfig.addEventListener('click', async function() {
const apiUrl = document.getElementById('apiUrl').value.trim(); const url = apiUrl.value.trim();
const apiKey = document.getElementById('apiKey').value.trim(); const key = apiKey.value.trim();
const apiModel = document.getElementById('apiModel').value.trim(); const model = apiModel.value.trim();
if (!apiUrl) { if (!url) {
alert('请输入API URL'); alert('请输入API URL');
return; return;
} }
if (!apiKey) { if (!key) {
alert('请输入API Key'); alert('请输入API Key');
return; return;
} }
this.textContent = '测试中...';
this.disabled = true;
try { try {
// 使用后端代理进行API测试 testApiConfig.disabled = true;
testApiConfig.textContent = '测试中...';
const response = await fetch('/test_api_connection', { const response = await fetch('/test_api_connection', {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json' 'Content-Type': 'application/json',
}, },
body: JSON.stringify({ body: JSON.stringify({
api_url: apiUrl, api_url: url,
api_key: apiKey, api_key: key,
api_model: apiModel api_model: model
}) })
}); });
const data = await response.json(); const result = await response.json();
if (response.ok && data.success) { if (result.success) {
alert('API连接成功'); alert(result.message);
// 如果勾选了保存配置则保存到localStorage
if (saveApiConfig.checked) {
saveApiConfigToLocalStorage();
}
} else { } else {
alert(`API连接失败: ${data.message || '未知错误'}`); alert(result.message);
} }
} catch (error) { } catch (error) {
alert(`API连接测试失败: ${error.message}`); alert(error.message);
} finally { } finally {
this.textContent = '测试连接'; testApiConfig.disabled = false;
this.disabled = false; testApiConfig.textContent = '测试连接';
}
});
// 监听输入变化,自动保存配置
[apiUrl, apiKey, apiModel].forEach(input => {
input.addEventListener('change', function() {
if (saveApiConfig.checked) {
saveApiConfigToLocalStorage();
}
});
});
// 监听保存配置复选框变化
saveApiConfig.addEventListener('change', function() {
if (this.checked) {
saveApiConfigToLocalStorage();
} else {
localStorage.removeItem('apiConfig');
} }
}); });
}); });
// 保存API配置到localStorage
function saveApiConfigToLocalStorage() {
const apiConfig = {
url: document.getElementById('apiUrl').value.trim(),
model: document.getElementById('apiModel').value.trim(),
key: document.getElementById('apiKey').value.trim(),
saveEnabled: true
};
localStorage.setItem('apiConfig', JSON.stringify(apiConfig));
}
// 从localStorage加载API配置
function loadApiConfig() {
const savedConfig = localStorage.getItem('apiConfig');
if (savedConfig) {
try {
const config = JSON.parse(savedConfig);
if (config.url) document.getElementById('apiUrl').value = config.url;
if (config.model) document.getElementById('apiModel').value = config.model;
if (config.key) document.getElementById('apiKey').value = config.key;
document.getElementById('saveApiConfig').checked = config.saveEnabled || false;
} catch (error) {
console.error('加载API配置时出错:', error);
}
}
}
</script> </script>
<script> <script>
// 添加 Markdown 样式 // 添加 Markdown 样式

View File

@@ -163,20 +163,20 @@ def test_api_connection():
# 检查响应 # 检查响应
if response.status_code == 200: if response.status_code == 200:
logger.info(f"API连接测试成功: {response.status_code}") logger.info(f"API 连接测试成功: {response.status_code}")
return jsonify({'success': True, 'message': '连接成功'}) return jsonify({'success': True, 'message': 'API 连接测试成功'})
else: else:
error_message = response.json().get('error', {}).get('message', '未知错误') error_message = response.json().get('error', {}).get('message', '未知错误')
logger.warning(f"API连接测试失败: {response.status_code} - {error_message}") logger.warning(f"API连接测试失败: {response.status_code} - {error_message}")
return jsonify({'success': False, 'message': f'连接失败: {error_message}', 'status_code': response.status_code}), 400 return jsonify({'success': False, 'message': f'API 连接测试失败: {error_message}', 'status_code': response.status_code}), 400
except requests.exceptions.RequestException as e: except requests.exceptions.RequestException as e:
logger.error(f"API连接请求错误: {str(e)}") logger.error(f"API 连接请求错误: {str(e)}")
return jsonify({'success': False, 'message': f'请求错误: {str(e)}'}), 400 return jsonify({'success': False, 'message': f'请求错误: {str(e)}'}), 400
except Exception as e: except Exception as e:
logger.error(f"测试API连接时出错: {str(e)}") logger.error(f"测试 API 连接时出错: {str(e)}")
logger.exception(e) logger.exception(e)
return jsonify({'success': False, 'message': f'测试连接时出错: {str(e)}'}), 500 return jsonify({'success': False, 'message': f'API 测试连接时出错: {str(e)}'}), 500
if __name__ == '__main__': if __name__ == '__main__':
logger.info("股票分析系统启动") logger.info("股票分析系统启动")