瀏覽代碼

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

hizhangling 2 周之前
父節點
當前提交
9b783feac1
共有 3 個文件被更改,包括 64 次插入10 次删除
  1. 1 0
      package.json
  2. 二進制
      src/assets/images/common/ai/logo.png
  3. 63 10
      src/views/xjc-integratedmachine/common/ai/chat.vue

+ 1 - 0
package.json

@@ -29,6 +29,7 @@
     "js-beautify": "1.14.11",
     "js-cookie": "3.0.5",
     "jsencrypt": "3.3.2",
+    "marked": "^16.1.1",
     "nprogress": "0.2.0",
     "pinia": "3.0.2",
     "splitpanes": "4.0.4",

二進制
src/assets/images/common/ai/logo.png


+ 63 - 10
src/views/xjc-integratedmachine/common/ai/chat.vue

@@ -1,21 +1,32 @@
 <template>
-    <div>
-        <div class="content-box">
-            <pre>{{ streamData }}</pre>
-            <span class="dot"></span>
-            <span class="dot"></span>
+    <div class="app-layout">
+        <div class="sidebar">
+            <div class="logo-section">
+                <img src="@/assets/images/common/ai/logo.png" alt="新基础 小新" width="160" height="160" />
+                <span class="logo-text">新基础 小新</span>
+            </div>
+        </div>
+        <div>
+            <div class="content-box">
+                <div v-html="streamHtmlData"></div>
+<!--                <span>{{streamData}}</span>-->
+                <span class="loading-dots"></span>
+                <span class="dot"></span>
+                <span class="dot"></span>
+            </div>
+            <el-button @click="stopMessage">停止回答</el-button>
         </div>
-
-        <el-button @click="stopMessage">停止回答</el-button>
     </div>
 </template>
 
 <script setup>
     import {getAiChatRecordList} from '@/api/xjc-integratedmachine/common/aiChat.js'
     import { getToken } from '@/utils/auth'
+    import { marked } from 'marked'
 
     let data = ref()
     const streamData = ref(''); // 存储流式数据
+    let streamHtmlData = ref(''); // 存储流式数据
     const isLoading = ref(false); // 加载状态
     let reader = null; // 读取器实例
     let controller = null; // AbortController用于中止请求
@@ -40,7 +51,7 @@
 
             // 请求体
             let form = {
-                "content": "你是谁?"
+                "content": "生成一个高中学习计划表格"
             }
             // 发送fetch请求
             const response = await fetch('/dev-api/ai/chat/stream', {
@@ -63,8 +74,9 @@
 
                 // 解码并追加数据
                 let chunk = decoder.decode(value, { stream: true });
-                chunk = chunk.replace(/\n/g, '').replace(/data:/g, '')
+                chunk = chunk.replace(/\n\n/g, ' ').replace(/data:/g, '')
                 streamData.value += chunk;
+                streamHtmlData.value = marked(streamData.value)
             }
         }catch (error) {
             // 如果是手动中止,不显示错误
@@ -73,6 +85,7 @@
                 streamData.value = 'Error: ' + error.message;
             }
         } finally {
+            streamHtmlData.value = marked(streamData.value)
             isLoading.value = false;
         }
     }
@@ -104,8 +117,29 @@
 </script>
 
 <style scoped>
+    .app-layout {
+        display: flex;
+        height: 100vh;
+    }
+
+    .sidebar {
+        width: 200px;
+        background-color: #f4f4f9;
+        padding: 20px;
+        display: flex;
+        flex-direction: column;
+        align-items: center;
+    }
+
+    .logo-section {
+        display: flex;
+        flex-direction: column;
+        align-items: center;
+    }
+
     .content-box {
-        max-height: 300px;
+        /*max-height: 300px;*/
+        width: 80vw;
         overflow-y: auto;
         border: 1px solid #eee;
         padding: 10px;
@@ -116,6 +150,9 @@
         white-space: pre-wrap;
         word-wrap: break-word;
     }
+    .loading-dots {
+        padding-left: 5px;
+    }
     .dot {
         display: inline-block;
         margin-left: 5px;
@@ -125,4 +162,20 @@
         border-radius: 50%;
         animation: pulse 1.2s infinite ease-in-out both;
     }
+    .dot:nth-child(2) {
+        animation-delay: -0.6s;
+    }
+
+    @keyframes pulse {
+        0%,
+        100% {
+            transform: scale(0.6);
+            opacity: 0.4;
+        }
+
+        50% {
+            transform: scale(1);
+            opacity: 1;
+        }
+    }
 </style>