【流式输出,前端如何接收】【模拟打字机打印效果】

艾玛钓到一只猫 2024-09-13 13:03:01 阅读 78

解决流式输出接口获取数据后,模拟打印效果

技术:vue js

setTimeOut和setInterval都不好用,因为流式输出的时间是不稳定的,导致打出的字容易乱序,

尝试多种方案后寻到如下方案:这是文心一言给的方案, 靠谱咱就是说。

<code><template>

<div>

<button @click="startPrinting">开始打印</button> code>

<p>{ -- -->{ currentChar }}</p>

</div>

</template>

<script>

class Queue {

constructor() {

this.items = [];

}

// 入队操作

enqueue(element) {

this.items.push(element);

}

// 出队操作并返回元素

dequeue() {

if (this.isEmpty()) {

return null;

}

return this.items.shift();

}

// 检查队列是否为空

isEmpty() {

return this.items.length === 0;

}

}

export default {

data() {

return {

queue: new Queue(),

text: "Hello, Vue.js!",

currentChar: '',

printingInterval: null,

};

},

methods: {

startPrinting() {

// 停止之前的打印(如果有的话)

if (this.printingInterval) {

clearInterval(this.printingInterval);

}

// 清空当前字符和队列

this.currentChar = '';

this.queue.items = [];

// 将字符串的每个字符入队

for (let char of this.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 = new EventSource(

"/base/business/api/v1/stream/connect?clientId=" + clientId

);

this.eventSource.onmessage = (event) => {

console.log("onmessage: " + event.data);code>

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) {

throw new Error("网络请求失败");

}

// // 创建一个阅读器来读取流式数据

// 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 = "";

},



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。