|
@@ -1,7 +1,102 @@
|
|
<template>
|
|
<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>
|
|
</template>
|
|
|
|
|
|
|
|
+<script setup>
|
|
|
|
+ import {getAiChatRecordList} from '@/api/xjc-integratedmachine/common/aiChat.js'
|
|
|
|
+ import { getToken } from '@/utils/auth'
|
|
|
|
+
|
|
|
|
+ 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>
|
|
<script>
|
|
export default {
|
|
export default {
|
|
name: "chat"
|
|
name: "chat"
|
|
@@ -9,5 +104,25 @@
|
|
</script>
|
|
</script>
|
|
|
|
|
|
<style scoped>
|
|
<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>
|
|
</style>
|