【流式输出,前端如何接收】【模拟打字机打印效果】
艾玛钓到一只猫 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 = "";
},
声明
本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。