微信小程序-接入sse数据流并实现打字机效果( ChatGPT )

从流中获取的数据格式如下

图1

小程序调用SSE接口

const requestTask = wx.request({url: `xxx`, // 需要请求的接口地址enableChunked: true, // enableChunked必须为truemethod: "GET",timeout: '120000',success(res) {console.log(res.data)},fail: function (error) {// 请求失败的操作console.error(error);},complete: function () {// 请求完成的操作,无论成功或失败都会执行console.log('请求完成', str);}})// 监听服务端返回的数据requestTask.onChunkReceived(res => {console.log( res, res.data);})

我这边接收到的数据类型为Uint8Array,需要处理成text文本(如上图)

在这里插入图片描述

 // 监听服务端返回的数据requestTask.onChunkReceived(res => {console.log( res, res.data);// Uint8Array转为text格式let arrayBuffer = res.data;let decoder = new TextDecoder('utf-8');let text = decoder.decode(arrayBuffer);//正则匹配上所有event:data后面的文字const eventRegex = /event:data\ndata:"data:(.*?)"/g;const eventRegexErr = /event:600\ndata:"(.*?)"/g;let matches = [];let match;if (text.indexOf('600') != -1) {//如果获取响应失败while ((match = eventRegexErr.exec(text)) !== null) {wx.showToast({title: match[1],})matches.push(match[1]);}str = str + matches.join('')} else {//如果获取响应成功while ((match = eventRegex.exec(text)) !== null) {matches.push(match[1]);}//处理成字符串str = str + matches.join('')console.log(text, str);}})

使对话有打字机效果

参考自:小程序实现 ChatGPT 聊天打字兼自动滚动效果

 handleRequestResolve(result) {this.setData({currentContent: ''})const contentCharArr = result.trim().split("")this.showText(0, contentCharArr);},showText(key = 0, value) {/* 所有内容展示完成 */if (key >= value.length) {// wx.vibrateShort()//判断字是否展示完this.setData({isShowFinish: true})return;}/* 渲染回话内容 */this.setData({currentContent: this.data.currentContent + value[key],})setTimeout(() => {/* 递归渲染内容 */this.showText(key + 1, value);}, 50);},

对话滚动到可视区域内

 handleScollTop() {return new Promise((resolve) => {const query = wx.createSelectorQuery()query.select('.page-content').boundingClientRect()query.select('.scroll-view-content').boundingClientRect()query.exec((res) => {const scrollViewHeight = res[0].heightconst scrollContentHeight = res[1].heightif (scrollContentHeight > (scrollViewHeight - 200)) {const scrollTop = scrollContentHeight - scrollViewHeight + 200this.setData({scrollTop}, () => {resolve()})} else {resolve()}})})},showText(key = 0, value) {/* 所有内容展示完成 */if (key >= value.length) {// wx.vibrateShort()this.setData({isShowFinish: true})return;}/* 渲染回话内容 */this.setData({currentContent: this.data.currentContent + value[key],}, () => {this.handleScollTop().then(() => {setTimeout(() => {this.showText(key + 1, value);}, 20);})})},

完整代码

.wxml
<scroll-view scroll-y scroll-top="{{scrollTop}}" wx:else class="page-content {{isFirst ? '' : 'page-content-bg'}}"><view class="scroll-view-content"><view wx:for="{{talkArr}}" wx:key="index" class="talk-box1"><view class="talk-box-question" wx:if="{{item.isAnswer=='0'}}"><view class="left"><text class="left-content">{{item.content}}</text></view><image class="right" src="../images/user-icon.png" mode="aspectFill" /></view><view class="talk-box-reply" wx:else><image class="left" src="../images/ai-icon.png" mode="aspectFill" /><view class="right"><view class="right-content"><view wx:if="{{(index!=talkArr.length-1)}}">{{item.content}}</view><view wx:else><view wx:if="{{loading}}"><image class="loading" src="../images/loading-1.png" mode="aspectFill" /></view><view wx:else>{{currentContent}}</view></view></view></view></view></view></view></scroll-view>
.wxss
.page-content {width: 100%;margin-top: 48rpx;padding-top: 150rpx;
}.page-content-bg {background: #F5F6F7;height: 75%;padding-bottom: 280rpx;overflow: scroll;padding-top: 0;
}.scroll-view-content {padding-top: 50rpx;
}.talk-box {display: flex;
}.talk-box1 {width: 90%;margin: 0 auto;
}.talk-box .left {width: 80rpx;height: 80rpx;
}.talk-box .right {margin-left: 30rpx;flex: 1;}.talk-item {height: 92rpx;background: #F6FFF9;border-radius: 0rpx 20rpx 20rpx 20rpx;font-family: PingFang SC, PingFang SC;font-weight: 500;font-size: 28rpx;color: rgba(51, 51, 51, 0.9);text-align: left;display: flex;align-items: center;padding: 0 38rpx;
}.talk-box-question,
.talk-box-reply {width: 100%;display: flex;margin-bottom: 32rpx;
}.talk-box-question .left {flex: 1;display: flex;align-items: center;justify-content: flex-end;
}.left-content {background: linear-gradient(273deg, #44BE35 0%, #6ECB63 100%);box-shadow: 0rpx 2rpx 8rpx 0rpx rgba(0, 0, 0, 0.05);border-radius: 24rpx 0rpx 24rpx 24rpx;padding: 24rpx;font-family: PingFang SC, PingFang SC;font-weight: 400;font-size: 28rpx;color: #FFFFFF;line-height: 44rpx;text-align: left;
}.talk-box-question .right {margin-left: 30rpx;width: 80rpx;height: 80rpx;
}.talk-box-reply .left {width: 80rpx;height: 80rpx;
}.talk-box-reply .right {margin-left: 30rpx;flex: 1;display: flex;align-items: center;justify-content: flex-start;
}.right-content {background: #FFFFFF;box-shadow: 0rpx 2rpx 8rpx 0rpx rgba(0, 0, 0, 0.05);border-radius: 0rpx 24rpx 24rpx 24rpx;border: 2rpx solid #6ECB63;padding: 24rpx;font-family: PingFang SC, PingFang SC;font-weight: 400;font-size: 28rpx;color: rgba(0, 0, 0, 0.9);line-height: 46rpx;text-align: left;
}
.js
 data: {isShowFinish: false,scrollTop: '',currentContent: '',loading: false,talkArr: []},getDataStream(data) {let str = ''let that = thisthis.setData({loading: true,})// 基础库为2.33.0const requestTask = wx.request({enableChunked: true, // 开启分片模式url: `xxx`, // 需要请求的接口地址enableChunked: true, // enableChunked必须为truemethod: "GET",responseType: "arraybuffer",timeout: '120000',success(res) {},fail: function (error) {// 请求失败的操作console.error(error);},complete: function () {// 请求完成的操作,无论成功或失败都会执行that.handleRequestResolve(str)let index = that.data.talkArr.length - 1let answerContent = `talkArr[${index}].content`that.setData({[answerContent]: str,loading: false})}})// 监听服务端返回的数据requestTask.onChunkReceived(res => {// Uint8Array转为text格式let arrayBuffer = res.data;let decoder = new TextDecoder('utf-8');let text = decoder.decode(arrayBuffer);//正则匹配上所有event:data后面的文字const eventRegex = /event:data\ndata:"data:(.*?)"/g;const eventRegexErr = /event:600\ndata:"(.*?)"/g;let matches = [];let match;if (text.indexOf('600') != -1) { //如果获取响应失败while ((match = eventRegexErr.exec(text)) !== null) {wx.showToast({title: match[1],icon: 'none'})matches.push(match[1]);}str = str + matches.join('')} else { //如果获取响应成功while ((match = eventRegex.exec(text)) !== null) {matches.push(match[1]);}//处理成字符串str = str + matches.join('')}})requestTask.offChunkReceived(res => {})},handleScollTop() {return new Promise((resolve) => {const query = wx.createSelectorQuery()query.select('.page-content').boundingClientRect()query.select('.scroll-view-content').boundingClientRect()query.exec((res) => {const scrollViewHeight = res[0].heightconst scrollContentHeight = res[1].heightif (scrollContentHeight > (scrollViewHeight - 200)) {const scrollTop = scrollContentHeight - scrollViewHeight + 200this.setData({scrollTop}, () => {resolve()})} else {resolve()}})})},handleRequestResolve(result) {this.setData({currentContent: ''})const contentCharArr = result.trim().split("")this.setData({isShowFinish: false})this.showText(0, contentCharArr);},showText(key = 0, value) {/* 所有内容展示完成 */if (key >= value.length) {// wx.vibrateShort()this.setData({isShowFinish: true})return;}/* 渲染回话内容 */this.setData({currentContent: this.data.currentContent + value[key],}, () => {this.handleScollTop().then(() => {setTimeout(() => {this.showText(key + 1, value);}, 20);})})},

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/809684.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

农场大乐斗游戏演示

功能介绍 农场系统 种菜操作&#xff1a;用户可以在农场中种植农作物&#xff0c;并进行浇水、杀虫、除草等维护操作。干旱、虫害、杂草都会影响农作物的生长速度和产量。农作物成熟后&#xff0c;用户需要及时收取&#xff0c;否则会在24小时后枯死&#xff0c;但可通过观看…

python通过url爬取视频资源到本地

很久之前我写过一篇通过url爬取图片地址的博文&#xff0c;说实在的&#xff0c;利用python实现自动化爬取资源确实很香。我本身是搞前端的。所以对py只是简单看了下&#xff0c;并不算多了解。因此之前写的那篇博文内容也很简单。简单到新手一看就会&#xff0c;一学就能用。方…

【C 数据结构】循环链表

文章目录 【 1. 基本原理 】【 2. 循环链表的创建 】2.1 循环链表结点设计2.2 循环单链表初始化 【 3. 循环链表的 插入 】【 4. 循环单链表的 删除操作 】【 5. 循环单链表的遍历 】【 6. 实例 - 循环链表的 增删查改 】【 7. 双向循环链表 】 【 1. 基本原理 】 对于单链表以…

刷代码随想录有感(29):用队列实现栈

不难理解&#xff0c;题干如下&#xff1a; 代码如下&#xff1a; class MyStack { public:queue<int> q;MyStack() {}void push(int x) {q.push(x);}int pop() {int size q.size();size--;while(size--){q.push(q.front());q.pop();}int res q.front();q.pop();retur…

kali桥接校园网实现上网

1.查看校园网信息 1. vim /etc/network/interfaces 添加下列信息&#xff0c;地址、网关、掩码和主机一样即可 3.vim /etc/resolv.conf 添加dns解析 4. /etc/init.d/networking restart 重启网络即可

探索AI工具导航网站

在现代科技发展迅猛的时代&#xff0c;人工智能&#xff08;AI&#xff09;已经成为了各行各业中不可或缺的一部分。了解和利用最新的AI工具对于工作、学习和娱乐都具有重大意义。在这篇博客中&#xff0c;我们将探索一些最新的人工智能工具导航网站&#xff0c;以及其中一款名…

【力扣】125.验证回文串

刷题&#xff0c;过了真的好有成就感&#xff01;&#xff01;&#xff01; 题解&#xff1a; 根据题目要求&#xff0c;我们需要处理一下几个问题&#xff1a; 将大写字母转变成小写对原来的字符串进行处理&#xff0c;只要字母和数字考虑只有一个和字符串为空的情况 1、将…

docker最简单教程(使用dockerfile构建环境)

一 手里有的东西 安装好的docker+dockerfile 二 操作 只需要在你的dockerfile文件下执行命令 docker build -t="xianhu/centos:gitdir" . 将用户名、操作系统和tag进行修改就可以了,这就相当于在你本地安装了一个docker环境,然后执行 docker run -it xianhu/ce…

第10天:基础入门-HTTP数据包Postman构造请求方法请求头修改状态码判断

第十天 一、HTTP/S 数据包请求与返回 数据-方法&头部&状态码 常规请求-Get——>访问网页获取资源用户登录-Post——>提交数据进行验证 head&#xff1a;与服务器索与 get 请求 一致的相应&#xff0c;响应体不会返回&#xff0c;获取包含在小消息头中的原信息&…

Spring Web MVC的入门学习(二)

本篇接着Spring Web MVC的入门学习&#xff08;一&#xff09;-CSDN博客来继续学习Spring MVC。 一、从请求中获取Header 1、传统获取 header 获取Header也是从 HttpServletRequest 中获取。 代码&#xff1a; import jakarta.servlet.http.HttpServletRequest; import jakar…

vue3+vite+typescript+pinia+element_plus构建web项目

1.vite搭建 yarn create vite 可能会提示node版本不支持&#xff0c;需要根据提示升级或降级node版本 使用nvm下载对应版本 nvm download 18.x.xnvm use 18.x.x// 需要安装yarn npm install -g yarn// 重新执行 yarn create vite 过程中会提供选择&#xff0c;分别选择vue、…

MySQL 实例student表综合查询

目录 例题&#xff1a; 1、查询student表的所有记录 2、查询student表的第2条到4条记录 3、从student表查询所有学生的学号&#xff08;id&#xff09;、姓名&#xff08;name&#xff09;和院系&#xff08;department&#xff09;的信息 4、从student表中查询计算机系和英…

逆向案例二十一——遇到混淆怎么办

开始新的板块尝试&#xff0c;混淆了怎么办 网址&#xff1a;极简壁纸_海量电脑桌面壁纸美图_4K超高清_最潮壁纸网站 抓包抓到&#xff0c;好久没做解密了&#xff0c;奥里给干他&#xff01;&#xff1a; 搜索关键字&#xff0c;打上断点&#xff0c;点击第二页。 _0x10a345…

关于光模块SFP-10G-SR、SFP-10G-LRM和SFP-10G-LR的对比分析

万兆光模块是万兆网络搭建领域中的重要组成部分&#xff0c;是传输万兆速率必要组件。随着网络速率和容量需求的增加&#xff0c;目前万兆光模块的应用量非常大。而在万兆光模块中&#xff0c;短距离光模块的出货量居首&#xff0c;本文将详细介绍3款短距离万兆光模块SFP-10G-S…

PyCharm Pro 2024:卓越的Python编辑开发工具,适用于Mac与Windows平台

PyCharm Pro 2024是一款专为Python开发者设计的强大编辑开发工具&#xff0c;无论是Mac还是Windows用户&#xff0c;都能从中受益良多。该软件凭借其出色的性能、丰富的功能和卓越的用户体验&#xff0c;成为Python编程界的翘楚。 作为一款高效的Python编辑器&#xff0c;PyCh…

什么是MOV视频格式?如何把MP4视频转MOV视频格式?

一&#xff0c;前言 当然可以&#xff0c;MP4视频可以转换为MOV格式。这两种格式都是常见的视频文件格式&#xff0c;它们都可以用于存储和播放视频内容。虽然它们的编码方式和特性有所不同&#xff0c;但使用合适的视频转换工具可以轻松地将MP4视频转换为MOV格式。 二&#…

React-样式使用

​&#x1f308;个人主页&#xff1a;前端青山 &#x1f525;系列专栏&#xff1a;React篇 &#x1f516;人终将被年少不可得之物困其一生 依旧青山,本期给大家带来React篇专栏内容:React-样式使用 目录 1、行内样式 2、使用className属性 3、css module模块化 4、styled-c…

sidusv指标,fpmarkets澳福愿称之为最强辅助指标

做投资的最怕的就是犹豫不定&#xff0c;抓不住交易的机会&#xff0c;最后又后悔不及。现在不用怕了&#xff0c;fpmarkets澳福今天分享愿称之为最强辅助指标——sidusv指标。可以帮助投资者轻松把握交易时机。 sidusv指标通过箭头指示进入点;红色的是卖出位置&#xff0c;绿色…

linux学习:结构体、联合体、枚举

目录 结构体 例子 大小 联合体 例子 大小 枚举 例子 大小 结构体 结构体就是我们自己发明的数据类型&#xff0c;因此使用结构体至少包含两个步骤&#xff1a; 第一&#xff0c;创建一个自定义的结构体类型。 第二&#xff0c;用这个自己搞出来的类型定义结构体变量 …

如何激怒一位Python爱好者?

写代码不那么pythonic风格的&#xff0c;多多少少都会让人有点难受。 什么是pythonic呢&#xff1f;简而言之&#xff0c;这是一种写代码时遵守的规范&#xff0c;主打简洁、清晰、可读性高&#xff0c;符合PEP 8&#xff08;Python代码样式指南&#xff09;约定的模式。 Pyt…