Uniapp仿ChatGPT Stream流式输出(非Websocket)
前言:流式输出可以使用websocket也可以使用stream来实现EventSource是 HTML5 中的一个接口,用于接收服务器发送的事件流(Server - Sent Events,SSE)。它提供了一种从服务器单向推送实时数据到 Web 页面的方式,使得 Web 应用能够以类似于事件驱动的模式获取更新,而不需要通过传统的轮询方式(不断地向服务器询问是否有新数据)。
请求和接收
- 使用代码
javascript
data (){
return {
task:null,
msg:"" ,
dataList:[], // 聊天内容
}
},
methods:{
askAgain() {
if (this.msg) {
this.$util.showToast(`请输入您的问题`)
return
}
this.$refs.paging.scrollToBottom()
setTimeout(() => {
if (!item) {
this.dataList.unshift({
content: msg, // 消息内容
type: 1, // 此为消息类别,设 1 为发出去的消息,0 为收到对方的消息,
end: true,
name: 'me',
list: [],
id: new Date().getTime()
})
}
this.dataList.unshift({
content: '', // 消息内容
content1: '', // 消息内容
type: 0, // 此为消息类别,设 1 为发出去的消息,0 为收到对方的消息,
end: false,
name: 'ai',
id: new Date().getTime()
})
this.toSend(msg);
this.$refs.paging.scrollToBottom()
}, 300)
},
toSend(msg) {
const that = this;
this.task = uni.request({
url: `${ getApp().globalData.requestUrl }ai/app/escort/ai/chat?question=${msg}`, // 会话接口
enableChunked: true, // 这意味着服务器会采用分块传输编码来发送数据。
method: 'POST',
timeout: 900000, // 设置超时时间为10秒
data: {
"question": msg, // 问题
"userId": uni.getStorageSync('wxopenid'), // 此处参数更具自己的参数填写
},
success: (res) => {
console.log(res)
if (res.statusCode == 200) {
// 本次回话是否完成
}
},
fail: () => {
}
})
// 监听流推送
this.task.onChunkReceived((res) => {
let uint8Array = new Uint8Array(res.data),
txt;
const type = Object.prototype.toString.call(uint8Array);
if (type === "[object Uint8Array]") {
txt = decodeURIComponent(escape(String.fromCharCode(...uint8Array)));
} else if (uint8Array instanceof ArrayBuffer) {
// 将ArrayBuffer转换为Uint8Array
const arr = new Uint8Array(uint8Array);
txt = decodeURIComponent(escape(String.fromCharCode(...arr)));
}
console.log(txt)
// 此处是我的逻辑 我推送的数据用^分割 所以要截取。
txt.split('^').forEach(it => {
if (it) {
let res = JSON.parse(it)
console.log(res)
that.dataList[0].content1 += res.data
}
})
});
},
}
- 以上为对话请求 和 流响应接收逻辑。
框架使用 z-paging
- 插件地址 z-paging
- 我的框架使用 z-paging 插件
文本回显 普通文本
javascript
<view class="text-warp">
{{ item.content }}
</view>
含有标签结构的文本回显 如img video 等
- 插件地址 zero-markdown-view
javascript
<zero-markdown-view :markdown="item.content"></zero-markdown-view>
流推送标签 会导致页面回显出标签 标签推送完才能展示出图片或视频
- 解决方法 声明新的变量接收 字符
- 遍历字符 当流中出现<img 字符开始截取 然后插入loading图片
- 当 图片标签尾部 /> 出现后 用截取的图片 替换掉 loading图片
- 以上为处理方法 , 可以解决zero-markdown-view 组件回显 带图片,标签的流数据时 闪烁的问题,以及标签的暴漏问题
流推送 字符长短不一,实现打字效果
- 每次推送调用递归方法 取一个字符
javascript
addNextCharacter() {
if (this.index < this.newVal.length) {
this.messages[0].content += this.newVal[this.index];
this.index++;
this.character = setTimeout(()=>{
this.addNextCharacter()
},Math.random() * 150 + 30)
}else{
clearTimeout(this.character)
this.character = null
if(!this.stratSet){
// 此处在请求完成后 清空所有的数据
}
}
},