解决流式输出接口获取数据后,模拟打印效果
技术:vue js
setTimeOut和setInterval都不好用,因为流式输出的时间是不稳定的,导致打出的字容易乱序,
尝试多种方案后寻到如下方案:这是文心一言给的方案, 靠谱咱就是说。
<template><div><button @click="startPrinting">开始打印</button><p>{{ currentChar }}</p></div></template><script>classQueue{constructor(){this.items =[];}// 入队操作 enqueue(element){this.items.push(element);}// 出队操作并返回元素 dequeue(){if(this.isEmpty()){returnnull;}returnthis.items.shift();}// 检查队列是否为空 isEmpty(){returnthis.items.length ===0;}}exportdefault{data(){return{queue:newQueue(),text:"Hello, Vue.js!",currentChar:'',printingInterval:null,};},methods:{startPrinting(){// 停止之前的打印(如果有的话) if(this.printingInterval){clearInterval(this.printingInterval);}// 清空当前字符和队列 this.currentChar ='';this.queue.items =[];// 将字符串的每个字符入队 for(let char ofthis.text){this.queue.enqueue(char);}// 开始打印 this.printingInterval =setInterval(()=>{const char =this.queue.dequeue();if(char){this.currentChar = char;}else{// 队列为空,停止打印 clearInterval(this.printingInterval);this.printingInterval =null;}},500);// 例如,每500毫秒打印一个字符 },},beforeDestroy(){// 在组件销毁前停止打印 if(this.printingInterval){clearInterval(this.printingInterval);}},};</script>
顺便提一下,模拟光标闪烁的css
::v-deep .shink_cursor {display: inline-block;width: 2px;height: 14px;
margin-bottom:-2px;
background-color: black;animation: blink 1s infinite;
@keyframes blink {0%{opacity:1;}50%{opacity:0;}100%{opacity:1;}}}
再附上流式接口调用代码:
// 流式对话// repeat 重新生成newChat(repeat =false){let clientId =this.chatUuid;this.flowMessage ={};//置空流式对话信息if(window.EventSource){this.eventSource =newEventSource("/base/business/api/v1/stream/connect?clientId="+ clientId
);this.eventSource.onmessage=(event)=>{
console.log("onmessage: "+ event.data);const dataJson =JSON.parse(event.data);if(dataJson.code ==200){if(dataJson.is_end ==="true"){this.eventSource.close();this.sendLoading =false;this.$store.dispatch("user/getInfo");// 刷新历史列表this.$EventBus.$emit("refreshHistory");
console.log("flowMessage: ",this.flowMessage);}this.showFlowText(dataJson, repeat);}else{this.delErrMsg(dataJson.msg);this.sendLoading =false;}};this.eventSource.onopen=(event)=>{
console.log("onopen:"+ event);};this.eventSource.onerror=(event)=>{
console.log("onerror:"+ event,this.eventSource);
console.log("对话中途中断~");};this.eventSource.close=(event)=>{
console.log("close :"+ event);this.sendLoading =false;};}else{
console.log("你的浏览器不支持SSE~");}
console.log(" 测试 打印");},// 假设这是你的流式数据获取函数// repeat 重新生成fetchData(repeat =false){let history =[];let question =this.sendMsg;if(!repeat){// 新问题if(!this.sendMsg.trim()){this.$message.warning("请输入问题");return;}const newDailogContent =[{role:"user",content:this.sendMsg,},];this.chatInfos =this.chatInfos.concat(newDailogContent);
history =[...this.chatInfos];}else{// 重新回答this.chatInfos[this.chatInfos.length -1].content ="";
history =this.chatInfos.slice(0,-1);const len = history.length;
question = history[len -1].content;}if(history.length >19){// 限制上下文长度
history = history.slice(history.length -19, history.length);}// 开启流式对话this.newChat(repeat);this.sendLoading =true;// 使用 Fetch API 发送带参数的 POST 请求const data ={clientId:this.chatUuid,
question,
history,regen: repeat ?2:1,//是否重新生成 1-正常,2-重新生成};chatFetchToServe(data)// .then((response) => response.text()) // 将响应体转为文本.then((response)=>{// 检查响应是否成功if(!response.ok){thrownewError("网络请求失败");}// // 创建一个阅读器来读取流式数据// const reader = response.body.getReader();// const dataContainer = document.getElementById('data-container');// // 定义一个函数来处理数据流中的文本片段// function handleTextFragment (fragment) {// const textNode = document.createTextNode(fragment);// dataContainer.appendChild(textNode);// }// // 定义一个函数来处理数据流中的完成事件// function handleDone () {// dataContainer.appendChild(document.createElement('br'));// console.log('数据流已接收完毕');// }// // 定义一个函数来处理数据流中的错误事件// function handleError (error) {// console.error('数据流处理出错:', error);// }// // 使用ReadableStream的getReader方法获取阅读器// const stream = response.body.getReader();// // 循环处理数据流中的文本片段// stream.read().then(({ done, value }) => {// if (done) {// handleDone();// } else {// handleTextFragment(value);// stream.releaseLock();// }// }).catch(handleError);}).catch((error)=>{
console.error("请求流式数据失败:", error);});this.sendMsg ="";},
版权归原作者 艾玛钓到一只猫 所有, 如有侵权,请联系我们删除。