Procházet zdrojové kódy

[feat][ai问答][实现fetch请求流式输出]

hizhangling před 3 týdny
rodič
revize
8d1f762dc9
1 změnil soubory, kde provedl 103 přidání a 4 odebrání
  1. 103 4
      src/views/xjc-integratedmachine/common/ai/chat.vue

+ 103 - 4
src/views/xjc-integratedmachine/common/ai/chat.vue

@@ -1,21 +1,100 @@
 <template>
     <div>
-        测试
+        <div class="content-box">
+            <pre>{{ streamData }}</pre>
+            <span class="dot"></span>
+            <span class="dot"></span>
+        </div>
+
+        <el-button @click="stopMessage">停止回答</el-button>
     </div>
 </template>
 
 <script setup>
-    import {ref} from 'vue'
     import {getAiChatRecordList} from '@/api/xjc-integratedmachine/common/aiChat.js'
+    import { getToken } from '@/utils/auth'
 
-    list();
     let data = ref()
+    const streamData = ref(''); // 存储流式数据
+    const isLoading = ref(false); // 加载状态
+    let reader = null; // 读取器实例
+    let controller = null; // AbortController用于中止请求
+
+
+    // 查看所有聊天记录
+    list();
     function list() {
         getAiChatRecordList().then(resp =>{
             console.log(resp)
             data.value = resp;
         })
     }
+
+    const sendMessage = async() => {
+        try{
+            isLoading.value = true;
+            streamData.value = ''; // 清空之前的数据
+
+            // 创建AbortController以便可以中止请求
+            controller = new AbortController();
+
+            // 请求体
+            let form = {
+                "content": "你是谁?"
+            }
+            // 发送fetch请求
+            const response = await fetch('/dev-api/ai/chat/stream', {
+                method: 'POST',
+                headers: {
+                    'Content-Type': 'application/json',
+                    'Authorization': 'Bearer ' + getToken()
+                },
+                body: JSON.stringify(form)
+            });
+
+            // 获取可读流的读取器
+            reader = response.body.getReader();
+            const decoder = new TextDecoder('utf-8');
+
+            // 循环读取流数据
+            while (true) {
+                const { done, value } = await reader.read();
+                if (done) break; // 如果流读取完成则退出循环
+
+                // 解码并追加数据
+                let chunk = decoder.decode(value, { stream: true });
+                chunk = chunk.replace(/\n/g, '').replace(/data:/g, '')
+                streamData.value += chunk;
+            }
+        }catch (error) {
+            // 如果是手动中止,不显示错误
+            if (error.name !== 'AbortError') {
+                console.error('流式读取失败:', error);
+                streamData.value = 'Error: ' + error.message;
+            }
+        } finally {
+            isLoading.value = false;
+        }
+    }
+
+    const stopMessage = () => {
+        if (reader) {
+            // 取消读取
+            reader.cancel().catch(() => {});
+            reader = null;
+        }
+        if (controller) {
+            // 中止请求
+            controller.abort();
+            controller = null;
+        }
+        isLoading.value = false;
+    }
+
+    onMounted(()=>{
+        sendMessage()
+    })
+
 </script>
 
 <script>
@@ -25,5 +104,25 @@
 </script>
 
 <style scoped>
-
+    .content-box {
+        max-height: 300px;
+        overflow-y: auto;
+        border: 1px solid #eee;
+        padding: 10px;
+        margin-top: 10px;
+        background: #f9f9f9;
+    }
+    pre {
+        white-space: pre-wrap;
+        word-wrap: break-word;
+    }
+    .dot {
+        display: inline-block;
+        margin-left: 5px;
+        width: 8px;
+        height: 8px;
+        background-color: #000000;
+        border-radius: 50%;
+        animation: pulse 1.2s infinite ease-in-out both;
+    }
 </style>