From 3421d0db47322fd9072a8606469e13b2b718e27f Mon Sep 17 00:00:00 2001 From: MT-Fire <798521692@qq.com> Date: Mon, 9 Mar 2026 14:25:38 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E9=98=BB=E6=AD=A2=E6=9C=AA=E7=99=BB?= =?UTF-8?q?=E5=BD=95=E7=94=A8=E6=88=B7=E4=BD=BF=E7=94=A8=E4=BB=BB=E4=BD=95?= =?UTF-8?q?=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/chat/ChatMain.vue | 14 ++++++++++++++ src/components/input/ChatInput.vue | 9 +++++++++ src/services/api.ts | 17 ++++++++++------- src/services/conversationApi.ts | 11 +++-------- src/services/request.ts | 9 ++++++++- src/stores/auth.ts | 7 ++++++- tsconfig.tsbuildinfo | 2 +- 7 files changed, 51 insertions(+), 18 deletions(-) diff --git a/src/components/chat/ChatMain.vue b/src/components/chat/ChatMain.vue index 2b26ea8..a48f475 100644 --- a/src/components/chat/ChatMain.vue +++ b/src/components/chat/ChatMain.vue @@ -52,6 +52,7 @@ import { ref, computed, watch, nextTick, onMounted } from "vue"; import { storeToRefs } from "pinia"; import { useChatStore } from "@/stores/chat"; import { useSettingsStore } from "@/stores/settings"; +import { useAuthStore } from "@/stores/auth"; import ChatHeader from "./ChatHeader.vue"; import MessageList from "./MessageList.vue"; import ChatInput from "@/components/input/ChatInput.vue"; @@ -65,6 +66,7 @@ defineEmits<{ const chatStore = useChatStore(); const settingsStore = useSettingsStore(); +const authStore = useAuthStore(); const { currentConversation, isStreaming } = storeToRefs(chatStore); const { settings, sidebarCollapsed } = storeToRefs(settingsStore); @@ -164,6 +166,12 @@ async function handleSend( systemPrompt?: string; }, ) { + // 检查认证状态 + if (!authStore.isAuthenticated) { + window.$toast?.('请先登录', 'error'); + return; + } + console.log("handleSend", text, attachments, options); // 检查是否还有正在上传的附件 const uploadingAttachments = attachments.filter((a) => a.uploading); @@ -337,6 +345,12 @@ function handleStop() { // 重试 async function handleRetry(messageId: string) { + // 检查认证状态 + if (!authStore.isAuthenticated) { + window.$toast?.('请先登录', 'error'); + return; + } + const message = messages.value.find((m: any) => m.id === messageId); if (!message || message.role !== MessageRole.ASSISTANT) return; diff --git a/src/components/input/ChatInput.vue b/src/components/input/ChatInput.vue index 1e78c93..080bbd0 100644 --- a/src/components/input/ChatInput.vue +++ b/src/components/input/ChatInput.vue @@ -173,6 +173,7 @@ import AttachmentPreview from "./AttachmentPreview.vue"; import { generateId } from "@/utils/helpers"; import type { Attachment } from "@/types/chat"; import { chatApi } from "@/services/api"; +import { useAuthStore } from "@/stores/auth"; interface AttachmentWithProgress extends Attachment { uploading?: boolean; @@ -216,6 +217,8 @@ const emit = defineEmits<{ }>(); // 响应式状态 +const authStore = useAuthStore(); + const inputText = ref(""); const attachments = ref([]); const isFocused = ref(false); @@ -350,6 +353,12 @@ async function addFileAsAttachment( file: File, type: "image" | "file" | "video", ) { + // 检查认证状态 + if (!authStore.isAuthenticated) { + window.$toast?.('请先登录', 'error'); + return; + } + const id = generateId(); // 创建本地预览URL diff --git a/src/services/api.ts b/src/services/api.ts index 2742c0a..09827c2 100644 --- a/src/services/api.ts +++ b/src/services/api.ts @@ -2,6 +2,8 @@ * Chat UI API 服务 * 所有端点都是固定的,后端需要实现这些端点 */ +import { getAuthHeaders } from './request'; + // API 端点定义(固定) const API_ENDPOINTS = { // 发送消息(流式) @@ -153,7 +155,7 @@ class ChatApi { { method: "POST", headers: { - "Content-Type": "application/json", + ...getAuthHeaders(), Accept: "text/event-stream", }, body: JSON.stringify(openAiRequest), @@ -244,9 +246,7 @@ class ChatApi { const response = await fetch(`${this.baseUrl}${API_ENDPOINTS.CHAT}`, { method: "POST", - headers: { - "Content-Type": "application/json", - }, + headers: getAuthHeaders(), body: JSON.stringify(requestBody), }); @@ -264,9 +264,7 @@ class ChatApi { async stopChat(messageId?: string) { await fetch(`${this.baseUrl}${API_ENDPOINTS.STOP}/${messageId}`, { method: "POST", - headers: { - "Content-Type": "application/json", - }, + headers: getAuthHeaders(), }); } @@ -326,8 +324,13 @@ class ChatApi { const formData = new FormData(); formData.append("file", file); + // 获取认证 headers,但不包含 Content-Type(让浏览器为 FormData 自动设置) + const authHeaders = getAuthHeaders(); + const { 'Content-Type': _, ...headersWithoutContentType } = authHeaders; + const response = await fetch(`${this.baseUrl}${API_ENDPOINTS.UPLOAD}`, { method: "POST", + headers: headersWithoutContentType, body: formData, }); diff --git a/src/services/conversationApi.ts b/src/services/conversationApi.ts index 7efe611..54e0551 100644 --- a/src/services/conversationApi.ts +++ b/src/services/conversationApi.ts @@ -1,10 +1,10 @@ /** * 对话 API 服务层 * - * 封装所有对话相关的后端 API 调用,支持认证预留 + * 封装所有对话相关的后端 API 调用 */ -import { authService } from './authService'; +import { getAuthHeaders } from './request'; import type { Conversation, Message, MessageContent, ConversationSettings } from '@/types/chat'; // API 端点 @@ -45,12 +45,7 @@ interface BackendMessage { * 获取请求头(包含认证信息) */ function getHeaders(): Record { - const headers: Record = { - 'Content-Type': 'application/json', - }; - // 添加认证 header(预留) - const authHeader = authService.getAuthHeader(); - return { ...headers, ...authHeader }; + return getAuthHeaders(); } /** diff --git a/src/services/request.ts b/src/services/request.ts index 1ab67bf..06658ee 100644 --- a/src/services/request.ts +++ b/src/services/request.ts @@ -50,7 +50,14 @@ export async function apiRequest( }, }; - return fetch(url, config); + const response = await fetch(url, config); + + // 401 认证失败提示 + if (response.status === 401) { + window.$toast?.('认证失败,请重新登录', 'error'); + } + + return response; } /** diff --git a/src/stores/auth.ts b/src/stores/auth.ts index 9983066..07af4b4 100644 --- a/src/stores/auth.ts +++ b/src/stores/auth.ts @@ -40,12 +40,18 @@ export const useAuthStore = defineStore('auth', () => { return null; } const data: AuthResponse = await response.json(); + if (data.success && data.data) { + window.$toast?.(`登录成功, 欢迎 ${data.data.nickname || data.data.username}`, 'success'); + return data.data; + }else{ + window.$toast?.('[Auth] Token 验证失败:Token无效'); } return null; } catch (error) { + console.error('[Auth] Token 验证失败:', error); return null; } @@ -73,7 +79,6 @@ export const useAuthStore = defineStore('auth', () => { const userInfo = await checkToken(tokenValue); if (userInfo) { - window.$toast?.(`登录成功, 欢迎 ${userInfo.nickname || userInfo.username}`, 'success'); token.value = tokenValue; user.value = userInfo; diff --git a/tsconfig.tsbuildinfo b/tsconfig.tsbuildinfo index 6d04746..b217ae1 100644 --- a/tsconfig.tsbuildinfo +++ b/tsconfig.tsbuildinfo @@ -1 +1 @@ -{"root":["./src/main.ts","./src/components/icons/index.ts","./src/composables/useKeyboard.ts","./src/services/api.ts","./src/services/authService.ts","./src/services/conversationApi.ts","./src/stores/chat.ts","./src/stores/settings.ts","./src/types/chat.ts","./src/utils/helpers.ts","./src/utils/migrateData.ts","./src/App.vue","./src/components/chat/ChatHeader.vue","./src/components/chat/ChatMain.vue","./src/components/chat/MessageList.vue","./src/components/chat/WelcomeScreen.vue","./src/components/input/AttachmentPreview.vue","./src/components/input/ChatInput.vue","./src/components/message/CodeBlock.vue","./src/components/message/MessageActions.vue","./src/components/message/MessageBubble.vue","./src/components/message/components/EChartsContainerNode.vue","./src/components/message/components/Loading.vue","./src/components/message/components/ThinkingNode.vue","./src/components/modals/ConversationSettingsModal.vue","./src/components/modals/SearchModal.vue","./src/components/modals/SettingsModal.vue","./src/components/modals/ShortcutsModal.vue","./src/components/sidebar/ChatSidebar.vue","./src/components/sidebar/ConversationItem.vue","./src/components/ui/FormSelect.vue","./src/components/ui/FormSlider.vue","./src/components/ui/FormSwitch.vue"],"errors":true,"version":"5.9.3"} \ No newline at end of file +{"root":["./src/main.ts","./src/components/icons/index.ts","./src/composables/useKeyboard.ts","./src/services/api.ts","./src/services/authService.ts","./src/services/conversationApi.ts","./src/services/request.ts","./src/stores/auth.ts","./src/stores/chat.ts","./src/stores/settings.ts","./src/types/chat.ts","./src/utils/helpers.ts","./src/utils/migrateData.ts","./src/App.vue","./src/components/chat/ChatHeader.vue","./src/components/chat/ChatMain.vue","./src/components/chat/MessageList.vue","./src/components/chat/WelcomeScreen.vue","./src/components/input/AttachmentPreview.vue","./src/components/input/ChatInput.vue","./src/components/message/CodeBlock.vue","./src/components/message/MessageActions.vue","./src/components/message/MessageBubble.vue","./src/components/message/components/EChartsContainerNode.vue","./src/components/message/components/Loading.vue","./src/components/message/components/ThinkingNode.vue","./src/components/modals/ConversationSettingsModal.vue","./src/components/modals/SearchModal.vue","./src/components/modals/SettingsModal.vue","./src/components/modals/ShortcutsModal.vue","./src/components/sidebar/ChatSidebar.vue","./src/components/sidebar/ConversationItem.vue","./src/components/ui/FormSelect.vue","./src/components/ui/FormSlider.vue","./src/components/ui/FormSwitch.vue"],"errors":true,"version":"5.9.3"} \ No newline at end of file