fix: 修复登录令牌和nginx中/test_api_connection接口

This commit is contained in:
CaasianVale
2025-03-08 03:09:35 +08:00
parent d545c94d66
commit 2dd8b7ded8
6 changed files with 190 additions and 48 deletions

View File

@@ -72,6 +72,15 @@ server {
proxy_read_timeout 300s; proxy_read_timeout 300s;
} }
location /test_api_connection {
proxy_pass http://backend:8888/test_api_connection;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_cache_bypass $http_upgrade;
}
# 所有其他路由返回index.htmlSPA应用需要 # 所有其他路由返回index.htmlSPA应用需要
location / { location / {
try_files $uri $uri/ /index.html; try_files $uri $uri/ /index.html;

View File

@@ -0,0 +1,59 @@
/* 全局样式覆盖 - 专门针对Naive UI组件 */
/* 移除所有Naive UI按钮的焦点边框 */
.n-button:focus,
.n-button:focus-visible {
outline: none !important;
box-shadow: none !important;
}
/* 为主要按钮添加自定义焦点样式 */
.n-button--primary:focus,
.n-button--primary:focus-visible {
outline: none !important;
box-shadow: 0 0 0 2px rgba(32, 128, 240, 0.2) !important;
}
/* 输入框和下拉菜单的焦点样式 */
.n-input:focus-within,
.n-input-number:focus-within,
.n-select:focus-within {
outline: none !important;
box-shadow: 0 0 0 2px rgba(32, 128, 240, 0.2) !important;
}
/* 标签的焦点样式 */
.n-tag:focus {
outline: none !important;
box-shadow: none !important;
}
/* 移除所有元素的黑色边框 */
*:focus {
outline: none !important;
}
/* 移除移动设备上的点击高亮 */
* {
-webkit-tap-highlight-color: transparent;
}
/* 修复Firefox特定的焦点样式 */
button::-moz-focus-inner,
input::-moz-focus-inner {
border: 0;
}
/* 确保所有按钮在点击后不显示边框 */
button:focus {
outline: none !important;
box-shadow: none !important;
}
/* 确保所有输入框在点击后显示自定义边框 */
input:focus,
textarea:focus,
select:focus {
outline: none !important;
box-shadow: 0 0 0 2px rgba(32, 128, 240, 0.2) !important;
}

View File

@@ -146,11 +146,17 @@
<div class="api-actions"> <div class="api-actions">
<div class="api-save-option"> <div class="api-save-option">
<n-switch <n-button
v-model:value="apiConfig.saveApiConfig" tertiary
@update:value="handleConfigChange" size="small"
/> @click="saveConfig"
<span class="save-label">保存配置到本地</span> round
>
<template #icon>
<n-icon :component="SaveIcon" />
</template>
保存配置到本地
</n-button>
</div> </div>
<div class="api-buttons"> <div class="api-buttons">
@@ -188,7 +194,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, computed, onMounted } from 'vue'; import { ref, computed, onMounted, markRaw } from 'vue';
import { import {
NButton, NButton,
NIcon, NIcon,
@@ -219,12 +225,14 @@ import {
RemoveOutline as RemoveIcon, RemoveOutline as RemoveIcon,
CheckmarkCircle as SuccessIcon, CheckmarkCircle as SuccessIcon,
CloseCircle as ErrorIcon, CloseCircle as ErrorIcon,
CodeSlashOutline as CodeIcon CodeSlashOutline as CodeIcon,
SaveOutline as SaveIcon
} from '@vicons/ionicons5'; } from '@vicons/ionicons5';
import { apiService } from '@/services/api'; import { apiService } from '@/services/api';
import { saveApiConfigToLocalStorage, loadApiConfig } from '@/utils'; import { saveApiConfigToLocalStorage, loadApiConfig } from '@/utils';
import type { ApiConfig } from '@/types'; import type { ApiConfig } from '@/types';
// 出于安全考虑API KEY不会显示在前端
const props = defineProps<{ const props = defineProps<{
defaultApiUrl?: string; defaultApiUrl?: string;
defaultApiModel?: string; defaultApiModel?: string;
@@ -276,7 +284,7 @@ const apiConfig = ref<ApiConfig>({
apiKey: '', apiKey: '',
apiModel: props.defaultApiModel || '', apiModel: props.defaultApiModel || '',
apiTimeout: props.defaultApiTimeout || '60', apiTimeout: props.defaultApiTimeout || '60',
saveApiConfig: false saveApiConfig: true // 默认启用保存配置
}); });
const apiTimeout = computed({ const apiTimeout = computed({
@@ -301,39 +309,35 @@ function toggleConfig() {
function handleConfigChange() { function handleConfigChange() {
console.log('API配置变更:', apiConfig.value); console.log('API配置变更:', apiConfig.value);
// 如果选择了保存配置,则自动保存 // 不再在每次配置变更时自动保存
if (apiConfig.value.saveApiConfig) { // 仅向父组件发送更新事件
saveApiConfigToLocalStorage({
apiUrl: apiConfig.value.apiUrl,
apiKey: apiConfig.value.apiKey,
apiModel: apiConfig.value.apiModel,
apiTimeout: apiConfig.value.apiTimeout,
saveApiConfig: true
});
}
// 向父组件发送更新事件
emit('update:apiConfig', { ...apiConfig.value }); emit('update:apiConfig', { ...apiConfig.value });
} }
function handleTimeoutChange(value: number | null) { function handleTimeoutChange(value: number | null) {
if (value !== null) { if (value !== null) {
apiConfig.value.apiTimeout = value.toString(); apiConfig.value.apiTimeout = value.toString();
handleConfigChange();
// 仅通知父组件更新,不自动保存
emit('update:apiConfig', { ...apiConfig.value });
} }
} }
function increaseTimeout() { function increaseTimeout() {
if (apiTimeout.value < 300) { if (apiTimeout.value < 300) {
apiTimeout.value += 10; apiTimeout.value += 10;
handleTimeoutChange(apiTimeout.value);
// 通知父组件
emit('update:apiConfig', { ...apiConfig.value });
} }
} }
function decreaseTimeout() { function decreaseTimeout() {
if (apiTimeout.value > 10) { if (apiTimeout.value > 10) {
apiTimeout.value -= 10; apiTimeout.value -= 10;
handleTimeoutChange(apiTimeout.value);
// 通知父组件
emit('update:apiConfig', { ...apiConfig.value });
} }
} }
@@ -377,25 +381,14 @@ async function testConnection() {
connectionStatus.value = { connectionStatus.value = {
type: 'success', type: 'success',
message: '连接成功API配置有效。', message: '连接成功API配置有效。',
icon: SuccessIcon icon: markRaw(SuccessIcon)
}; };
// 如果选择了保存配置,则保存
if (apiConfig.value.saveApiConfig) {
saveApiConfigToLocalStorage({
apiUrl: apiConfig.value.apiUrl,
apiKey: apiConfig.value.apiKey,
apiModel: apiConfig.value.apiModel,
apiTimeout: apiConfig.value.apiTimeout,
saveApiConfig: true
});
}
} else { } else {
message.error(`API连接测试失败: ${response.message}`); message.error(`API连接测试失败: ${response.message}`);
connectionStatus.value = { connectionStatus.value = {
type: 'error', type: 'error',
message: `连接失败: ${response.message}`, message: `连接失败: ${response.message}`,
icon: ErrorIcon icon: markRaw(ErrorIcon)
}; };
} }
} catch (error: any) { } catch (error: any) {
@@ -403,7 +396,7 @@ async function testConnection() {
connectionStatus.value = { connectionStatus.value = {
type: 'error', type: 'error',
message: `连接错误: ${error.message || '未知错误'}`, message: `连接错误: ${error.message || '未知错误'}`,
icon: ErrorIcon icon: markRaw(ErrorIcon)
}; };
} finally { } finally {
testingConnection.value = false; testingConnection.value = false;
@@ -416,16 +409,18 @@ function resetConfig() {
apiKey: '', apiKey: '',
apiModel: props.defaultApiModel || '', apiModel: props.defaultApiModel || '',
apiTimeout: props.defaultApiTimeout || '60', apiTimeout: props.defaultApiTimeout || '60',
saveApiConfig: false saveApiConfig: true
}; };
// 清除本地存储 // 清除本地存储
if (window.localStorage) { if (window.localStorage) {
localStorage.removeItem('apiConfig'); localStorage.removeItem('apiConfig');
message.success('已重置API配置并清除本地存储');
} else {
message.success('已重置API配置');
} }
connectionStatus.value = null; connectionStatus.value = null;
message.success('已重置API配置');
emit('update:apiConfig', { ...apiConfig.value }); emit('update:apiConfig', { ...apiConfig.value });
} }
@@ -433,7 +428,21 @@ function resetConfig() {
function selectModel(key: string) { function selectModel(key: string) {
console.log('选择模型:', key); console.log('选择模型:', key);
apiConfig.value.apiModel = key; apiConfig.value.apiModel = key;
handleConfigChange();
// 通知父组件
emit('update:apiConfig', { ...apiConfig.value });
}
// 显式保存配置的函数
function saveConfig() {
saveApiConfigToLocalStorage({
apiUrl: apiConfig.value.apiUrl,
apiKey: apiConfig.value.apiKey,
apiModel: apiConfig.value.apiModel,
apiTimeout: apiConfig.value.apiTimeout,
saveApiConfig: true
});
message.success('已将配置保存到本地');
} }
onMounted(() => { onMounted(() => {
@@ -528,15 +537,27 @@ onMounted(() => {
.api-save-option { .api-save-option {
display: flex; display: flex;
align-items: center; align-items: center;
background-color: rgba(0, 0, 0, 0.02);
padding: 6px 12px; padding: 6px 12px;
border-radius: 16px; border-radius: 16px;
} }
.save-label { .api-save-option button {
margin-left: 0.5rem; display: flex;
font-size: 0.875rem; align-items: center;
gap: 6px;
padding: 8px 16px;
font-weight: 500; font-weight: 500;
background-color: rgba(0, 0, 0, 0.02);
transition: all 0.2s ease;
}
.api-save-option button:hover {
background-color: rgba(32, 128, 240, 0.08);
transform: translateY(-1px);
}
.api-save-option button .n-icon {
font-size: 16px;
} }
.api-buttons { .api-buttons {

View File

@@ -603,16 +603,32 @@ async function analyzeStocks() {
requestData.api_timeout = apiConfig.value.apiTimeout; requestData.api_timeout = apiConfig.value.apiTimeout;
} }
// 获取身份验证令牌
const token = localStorage.getItem('token');
// 构建请求头
const headers: Record<string, string> = {
'Content-Type': 'application/json'
};
// 如果有令牌,添加到请求头
if (token) {
headers['Authorization'] = `Bearer ${token}`;
}
// 发送分析请求 // 发送分析请求
const response = await fetch('/analyze', { const response = await fetch('/analyze', {
method: 'POST', method: 'POST',
headers: { headers,
'Content-Type': 'application/json'
},
body: JSON.stringify(requestData) body: JSON.stringify(requestData)
}); });
if (!response.ok) { if (!response.ok) {
if (response.status === 401) {
message.error('未授权访问,请登录后再试');
// 可以在这里触发登录流程
return;
}
if (response.status === 404) { if (response.status === 404) {
throw new Error('服务器接口未找到,请检查服务是否正常运行'); throw new Error('服务器接口未找到,请检查服务是否正常运行');
} }

View File

@@ -1,5 +1,6 @@
import { createApp } from 'vue' import { createApp } from 'vue'
import './style.css' import './style.css'
import './assets/css/global.css'
import App from './App.vue' import App from './App.vue'
import router from './router' import router from './router'

View File

@@ -51,7 +51,8 @@ button:hover {
} }
button:focus, button:focus,
button:focus-visible { button:focus-visible {
outline: 4px auto -webkit-focus-ring-color; outline: none !important;
box-shadow: 0 0 0 2px rgba(32, 128, 240, 0.2) !important;
} }
.card { .card {
@@ -77,3 +78,38 @@ button:focus-visible {
background-color: #f9f9f9; background-color: #f9f9f9;
} }
} }
/* 全局移除黑色边框 */
*:focus {
outline: none !important;
}
/* 输入框和表单元素的焦点样式 */
input:focus,
textarea:focus,
select:focus {
outline: none !important;
box-shadow: 0 0 0 2px rgba(32, 128, 240, 0.2) !important;
}
/* 移除移动设备上的点击高亮 */
* {
-webkit-tap-highlight-color: transparent;
}
/* 修复Firefox特定的焦点样式 */
button::-moz-focus-inner,
input::-moz-focus-inner {
border: 0;
}
/* Naive UI组件的焦点样式 */
.n-button:focus,
.n-button:focus-visible,
.n-input:focus-within,
.n-input-number:focus-within,
.n-select:focus-within,
.n-tag:focus {
outline: none !important;
box-shadow: 0 0 0 2px rgba(32, 128, 240, 0.2) !important;
}