微信小程序 云开发 聊天功能

项目背景

。功能要求为:一对一聊天,可以发送文字,图片,语音,文件,视频,含消息列表页。 暑假没事干来写篇博客复盘一下。框架和样式部分就是采用了colorUI 的组件,没啥好说的,这方面我也不会

聊天室

成品展示

  

 这是聊天内容部分

<scroll-view class="cu-chat" scroll-y="true"  scroll-into-view="{{scrollId}}"
style="height:{{systemInfo.windowHeight - 50}}px;"enable-flex="true"><block wx:for="{{msgList}}" wx:key="index" wx:if="{{msgList!=[]}}"><view class="cu-chat"><!-- 我的部分 --><!-- 文字 --><block wx:if="{{item.creator == myOpenid && item.type == 'text'}}"><view class="cu-item self"><!-- 我的发送消息 --><view class="main"><view class="content bg-green shadow" id="msg-{{index}}"><text>{{item.content}}</text></view></view><!--我的头像  --><view class="cu-avatar radius" style="background-image:url({{myUserInfo.avaUrl}});"></view></view> </block><!-- 图片 --><block wx:if="{{item.creator == myOpenid && item.type == 'image'}}"><view class="cu-item self"  id="msg-{{index}}"><view class="main" bindtap="viewImage" data-url="{{item.content}}"><image src="{{item.content}}" class="radius" mode="widthFix"></image></view><view class="cu-avatar radius" style="background-image:url({{myUserInfo.avaUrl}});"></view></view></block><!-- 视频 --><block wx:if="{{item.creator == myOpenid && item.type == 'video'}}"><view class="cu-item self"  id="msg-{{index}}"><view class="main" bindtap="viewvideo" data-url="{{item.content}}"><video src="{{item.content}}" class="radius" mode="widthFix"></video></view><view class="cu-avatar radius" style="background-image:url({{myUserInfo.avaUrl}});"></view></view></block><!-- 文件 --><block wx:if="{{item.creator == myOpenid && item.type == 'file'}}"><view class="cu-item self"  id="msg-{{index}}"><view class="main" bindtap="viewFile" data-fileid="{{item.content.fileID}}"><view class="content" style="width: 500rpx;height: 200rpx;display: flex;justify-content: space-around;padding-left:30rpx;padding-right:40rpx;padding-top:0rpx"><view class="fileName" style="margin-right: 30rpx;"><view class="view" style="font-size: 30rpx; font-weight: 20rpx;">{{item.content.fileName}}</view></view><!-- 文件图标 --><view class="image" ><image class="cu-avatar radius" style="background-image:url({{fileImg}});width:70rpx;height: 80rpx;"></image></view></view> </view><view class="cu-avatar radius" style="background-image:url({{myUserInfo.avaUrl}});"></view></view></block><!-- 语音 --><block wx:if="{{item.creator == myOpenid && item.type == 'voice'}}"><view class="cu-item self"  id="msg-{{index}}"><view class="main" bindtap="voicePlay" data-voiceID="{{item.content.fileID}}"><view class="action text-bold text-grey">{{item.content.duration}}"</view><view class="content bg-green shadow"><text class="cuIcon-sound text-xxl padding-right-xl" > </text></view></view><!-- 头像 --><view class="cu-avatar radius" style="background-image:url({{myUserInfo.avaUrl}});"></view></view></block><!-- 他的部分 --><!-- 文字 --><block wx:if="{{item.creator !== myOpenid && item.type == 'text'}}"><view class="cu-item"><!-- 他的头像 --><view class="cu-avatar radius" style="background-image:url({{hisUserInfo}});"></view><!-- 他的消息 --><view class="main"><view class="content shadow" id="msg-{{index}}"><text>{{item.content}}</text></view></view></view></block><!-- 图片 --><block wx:if="{{item.creator !== myOpenid && item.type == 'image'}}"><view class="cu-item"  id="msg-{{index}}"><!-- 他的头像 --><view class="cu-avatar radius" style="background-image:url({{hisUserInfo}});"></view><!-- 他的图片 --><view class="main" bindtap="viewImage" data-url="{{item.content}}"><image src="{{item.content}}" class="radius" mode="widthFix"></image></view></view></block><!-- 视频 --><block wx:if="{{item.creator !== myOpenid && item.type == 'video'}}"><view class="cu-item "  id="msg-{{index}}"><!-- 他的头像 --><view class="cu-avatar radius" style="background-image:url({{hisUserInfo}});"></view><view class="main" bindtap="viewvideo" data-url="{{item.content}}"><video src="{{item.content}}" class="radius" mode="widthFix"></video></view></view></block><!-- 文件 --><block wx:if="{{item.creator !== myOpenid && item.type == 'file'}}"><view class="cu-item "  id="msg-{{index}}"><!-- 他的头像 --><view class="cu-avatar radius" style="background-image:url({{hisUserInfo}});"></view><!-- 他的文件 --><view class="main"bindtap="viewFile" data-fileid="{{item.content.fileID}}"><view class="content" style="width: 500rpx;height: 200rpx;display: flex;justify-content: space-around;padding-left:30rpx;padding-right:40rpx;padding-top:0rpx"><view class="fileName" style="margin-right: 30rpx;"><view class="view" style="font-size: 30rpx; font-weight: 20rpx;">{{item.content.fileName}}</view></view><!-- 文件图标 --><view class="image" ><image class="cu-avatar radius" style="background-image:url({{fileImg}});width:70rpx;height: 80rpx;"></image></view></view> </view></view></block><!-- 语音 --><block wx:if="{{item.creator !== myOpenid && item.type == 'voice'}}"><view class="cu-item" id="msg-{{index}}"><!-- 他的头像 --><view class="cu-avatar radius" style="background-image:url({{hisUserInfo}});"></view><!-- 他的消息 --><view class="main"bindtap="voicePlay" data-voiceID="{{item.content.fileID}}"><view class="content shadow" ><text class="cuIcon-sound text-xxl padding-right-xl"></text></view><view class="action text-bold text-grey">{{item.content.duration}}"</view></view></view></block></view></block>
</scroll-view>

浅浅地小讲一下

<scroll-view class="cu-chat" scroll-y="true"  scroll-into-view="{{scrollId}}"
style="height:{{systemInfo.windowHeight - 50}}px;"enable-flex="true"></scroll-view>
scroll-into-viewstring值应为某子元素id(id不能以数字开头)。设置哪个方向可滚动,则在哪个方向滚动到该元素

scrollId的值会在聊天内容发生变化时,变更为最后一条消息元素的id

msgList 是消息数组

 集合chat_msg储存了聊天数据(部分),其中一条记录保存一个会话房间的数据,下面为一条记录的示例

_id自动生成
session_id代码中用了唯一确定会话房间的字段, 志愿者用户首次联系负责人时将在chat_msg创建会话房间记录,生成session_id字段,session_id由负责人和志愿者的_openid连接而成)
msg_set会话房间的消息数组,数组成员为对象类型,按消息类型分类消息对象属性&消息类型文字图片视频文件语音
typetextimage

video

file

voice

content文字消息云ID
creator消息创建者的_openid
createTimeStamp消息发送时的时间戳

下面是图片、文件、语音消息的点击事件

  //点击图片预览viewImage(e){var urls = [e.currentTarget.dataset.url]wx.previewImage({current: '', // 当前显示图片的http链接urls: urls, // 需要预览的图片http链接列表showmenu : true})},//打开文件viewFile(e){var fileid = e.currentTarget.dataset.fileid;//云文件fileIDvar that = this;wx.cloud.downloadFile({fileID: fileid,}).then((res)=>{console.log('res',res.tempFilePath);wx.openDocument({filePath: res.tempFilePath,showMenu: true,}).then(()=>{}).catch((err)=>{console.log('文件预览失败',err);})})},//播放录音(该页面的js文件中全局变量  有const innerAudioContext   =wx.createInnerAudioContext();//播放语音)voicePlay(e){innerAudioContext.stop();innerAudioContext.src = '';console.log(e.currentTarget.dataset.voiceid)let id = e.currentTarget.dataset.voiceid;innerAudioContext.src = id;innerAudioContext.onPlay(()=>{console.log('语音播放')});innerAudioContext.onError((res)=>{console.log(res.errMsg) console.log(res.errCode)})innerAudioContext.play();},

底部消息编辑区

  <!-- 发送部分 -->
<view class="cu-bar foot input {{InputBottom!=0?'cur':''}}" style="bottom:{{InputBottom}}px"><!-- +更多 --><view class="action" bindtap="showModal"><text class="cuIcon-roundadd lg text-gray"></text></view><!-- text&voice --><view class="action" wx:if="{{inputMode == 'voice'}}" bindtap="switchInputMode"><text class="cuIcon-keyboard lg text-grey"></text></view><view class="action" wx:if="{{inputMode=='text'}}"bindtap="switchInputMode"><text class="cuIcon-sound text-grey"></text></view> <!-- 文字输入框 --><input hidden="{{inputMode=='voice'}}"class="solid-bottom" bindfocus="InputFocus" bindblur="InputBlur" adjust-position="{{false}}" focus="{{false}}" maxlength="300" cursor-spacing="10" value="{{inputContent}}" bindinput="onInputContent"></input><!-- 语音按住区 --><view hidden="{{inputMode=='text'}}"class="middle-view" bindtouchstart="RecordStart" catch:touchmove="RecordMove" bindtouchend="RecordEnd">{{voiceTip}}</view><!-- 发送按钮 --><button hidden="{{inputMode=='voice'}}"class="cu-btn bg-green shadow" bindtap="sendContent">发送</button>
</view><!-- 底部多选功能窗口 -->
<view class="cu-modal bottom-modal {{modalName=='bottomModal'?'show':''}}"><view class="cu-dialog"><view class="cu-bar bg-white"><view class="action text-blue" bindtap="hideModal">取消</view></view><view class="padding-xl"><view class="cu-list grid col-3"><view class="cu-item" bindtap="sendPic"><text class="cuIcon-pic lg text-gray"></text><text>图片</text></view><view class="cu-item" bindtap="sendVideo"><text class="cuIcon-video lg text-gray"></text><text>视频</text></view><view class="cu-item" bindtap="sendFile"><text class="cuIcon-send lg text-gray"></text><text>文件</text></view></view></view></view>
</view>
<!-- 录音中的图样 -->
<view class="voice-tip" hidden="{{!recording}}"><view><image src="{{recordimg}}"></image></view><view>{{showtip}}</view>
</view>

浅浅地小讲一下

inputMode这个变量用来表示当前的输入形式,如果是text显示文字输入框,如果是voice则显示语音输入框。bindtap="switchInputMode"这个点击事件用来切换输入方式。

发送的消息都会包装成msg对象作为对应云函数(lyc_consult  --  sendMsg )的参数,经由云函数处理,写入数据库集合的对应记录中,其中文字和图片消息还会经过合法性检验。通过监听对应会话记录达到实时聊天。

实时聊天实现

  onLoad:async function (e) {loading=true;if(msgWatcher) await msgWatcher.close();console.log('进入会话页,携带会话号',e.session_id);console.log("hisUserInfo",e.hisUserInfo);this.data.sid = e.session_id;this.setData({hisUserInfo: e.hisUserInfo,myUserInfo:App.globalData.userInfo.userInfo,})var that=this;//获取系统信息,得到可用窗口高度wx.getSystemInfo({success: function (res) {that.setData({systemInfo: res,})}})//开启集合chat_msg.session_id的消息监听setTimeout(this.initWatcher,1000)//初始化录音器this.initRecord();},//消息监听器开启async initWatcher(){wx.showLoading({title: '加载中...',mask: true,})console.log('initWatcher函数执行,消息监听器开启'); msgWatcher = await db.collection('chat_msg').where({session_id : this.data.sid,}).watch({onChange:this.onChange.bind(this),onError:function(err){console.log("onError",err);wx.showModal({title: '提示',content: '数据监听失败',showCancel:false,confirmText:'重新加载',success (res) {if (res.confirm) {that.onLoad();} }})}})},//消息监听回调函数onChange(snapshot){console.log('onChange监听回调函数的snapshot',snapshot)//即时聊天this.sortRenderMsg(snapshot.docs[0].msg_set)},//对消息对象数组进行时间排序并渲染async sortRenderMsg(e){console.log('排序渲染函数执行');var list=e.sort(function(a,b){return a.createTimeStamp-b.createTimeStamp});await this.setData({msgList : list,scrollId: 'msg-'+(list.length-1)});if(loading==true){wx.hideLoading({complete: () => {loading=false},})}},

消息列表页

 当会话房间有为读消息时,会有红点提示,且红点内数字即为未读消息条数。

由于只能志愿者主动去联系负责人,从而达到新开会话房间的行为,所以不必考虑有新的会话进来这种情况,只用监听已有的会话记录,负责人端则既需要考虑监听已有的会话房间,还要考虑监听负责人的会话房间数组的更新情况,及时显示首次来联系的志愿者消息。

  //监听我的所有项目会话(是否有项目负责人发来新消息)watch() {console.log('开启消息列表页的会话监听');var arr = sids;var that = this;watcher = db.collection('chat_msg').where({session_id: _.in(arr),}).watch({onChange: this.onChange.bind(this),onError: function (err) {console.log("onError", err);if(loading==false){wx.hideLoading({complete: () => {loading=true},})}wx.showModal({title: '提示',content: '数据监听失败',showCancel:false,confirmText:'重新加载',success (res) {if (res.confirm) {that.onShow();} }})}})},//监听回调函数onChange(snapshot) {console.log('消息列表页监听回调函数的snapshot', snapshot);const sessions = this.data.sessions;snapshot.docChanges.forEach(update);//对发生改变的会话记录进行了遍历function update(e) {const sid = e.doc.session_id;const index = sids.indexOf(sid);const msg_set = e.doc.msg_set;const len = msg_set.length;//如果该会话无任何消息,则跳出if (len == 0) return;const lastMsgTime = msg_set[len - 1].createTimeStamp;const leaveTimeStamp = sessions[index].leaveTimeST;let unReadNum = 0;if ((lastMsgTime > leaveTimeStamp) || (lastMsgTime == leaveTimeStamp)) {if (lastMsgTime == leaveTimeStamp) { unReadNum = 1; }if (lastMsgTime > leaveTimeStamp) {unReadNum = function () {let sum = 0;for (let x = len - 1 ; (msg_set[x].createTimeStamp > leaveTimeStamp) || (msg_set[x].createTimeStamp == leaveTimeStamp); --x) {++sum;}console.log('sum',sum)return sum;}();}db.collection('all_user').where({_openid: myOpenid,'mySession_ids.session_id': sid}).update({data: {'mySession_ids.$.unReadNum': parseInt(unReadNum)}})console.log('unReadNum',unReadNum)}else {console.log('会话' + sid + '无新消息');}}let that = this;setTimeout(f, 100)async function f() {await db.collection('all_user').where({_openid: myOpenid}).get().then((res) => {if (res.data.length) {that.setData({sessions: res.data[0].mySession_ids,});console.log('列表页渲染完成');}})if(loading==false){wx.hideLoading({complete: () => {loading=true},})}}},

开摆   这里是源码   https://gitee.com/YYCCbigbig/applet-chat-function.git

有用的话,点个赞吧

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

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

相关文章

借助云开发,利用订阅消息,云函数路由实现小程序好友一对一聊天,添加好友等仿微信功能

微信小程序借助云开发&#xff0c;利用订阅消息&#xff0c;云函数路由实现小程序好友一对一聊天&#xff0c;添加好友等仿微信功能 这篇文章已经进行了更新&#xff0c;请点此进行查看仿微信好友聊天**点此获取源码** 主要功能有后续可能补充功能详细介绍注意 这篇文章已经进行…

微信小程序云开发通过服务号给用户推送模板消息

主要参考下面两个文章 1、云函数接收公众号消息推送 | 微信开放社区 ​​​​​​2、微信小程序通过公众号(服务号)推送通知或提醒步骤及代码(一&#xff0c;获取推送前所需信息)_微信小程序推送服务通知_庆登登登的博客-CSDN博客 一些基本的概念就不讲了&#xff0c;这里主要…

微信小程序云开发———云函数

1.云函数环境配置 在目录中添加一个cloud文件夹与pages平级 并在project.config.json文件中配置云函数所在目录cloud/ 保存后cloud文件夹会出现一个云朵的形状&#xff0c;并且需要我们进行同步环境列表 因为作者有其他的云开发环境&#xff0c;所以正常来说同学们新建的cloud…

微信小程序消息推送

简介 微信小程序发送消息推送到用户微信上 基础流程 用户登录 &#xff1a;获取微信提供的openid 开发者去微信公众平台申请模板&#xff1a;获得模板id授权消息推送&#xff1a;在小程序中调用wx.requestSubscribeMessage方法成功后将数据保存到后台 消息推送表 中在需要发送…

再度警惕!AI“灭霸”要来了?Hinton、Bengio等300余大佬联名上书

来源&#xff1a;机器之心 人工智能存在「灭绝」级别的风险&#xff0c;未来的系统可能像核武器一样致命。本周二&#xff0c;著名的人工智能学者、科技公司创始人团结了起来&#xff0c;发出呼吁。 公开信地址&#xff1a;https://www.safe.ai/statement-on-ai-risk 前有马斯克…

【探索人工智能】我与讯飞星火认知大模型的对话

文章目录 讯飞星火认知大模型的地址概要讯飞星火认知大模型的发展历程讯飞星火认知大模型的主页利用讯飞星火大模型解决一些基本的数学问题讯飞星火认知大模型与OpenAI,ChatGPT没有关系&#xff01;让讯飞星火认知大模型编写传奇代码hello world小结 讯飞星火认知大模型的地址 …

在Ubuntu22.04里安装脉冲星信号处理工具----Pulsar软件包

在Ubuntu22.04安装Pulsar软件包 1.ubuntu 22.04 安装gcc-7、g7&#xff08;用来编译后续软件&#xff09; 1.1 添加镜像&#xff1a; 打开源镜像文件 sudo vim /etc/apt/sources.list在最后一行添加如下内容&#xff1a; deb [archamd64] http://archive.ubuntu.com/ubunt…

6-2. 实战项目:足球运动员分析

足球运动员分析 1. 项目背景 当前&#xff0c;足球运动是最受欢迎的运动之一&#xff08;也可以说没有之一&#xff09;。 我们的任务&#xff0c;就是在众多的足球运动员中&#xff0c;发现统计一些关于足球运动员的共性&#xff0c;或某些潜在的规律。也是好玩。 2. 数据…

【案例】足球运动员分析

文章目录 足球运动员分析案例需求数据集程序实现1. 导入相关库2. 加载相关数据集3. 数据探索与清洗3.1. 缺失值处理3.2. 异常值处理处理3.3. 重复值处理 4. 身高与体重处理5. 运动员身高&#xff0c;体重&#xff0c;评分信息分布6. 左脚与右脚选手在数量上是否存在偏差7. 从球…

足球运动员分析

数据集 数据集 背景&#xff1a; 我们关注足球运动员的如下内容&#xff1a; 足球运动员是否受出生日期的影响&#xff1f;左撇子适合踢足球吗&#xff1f;足球运动员的号码是否与位置有关&#xff1f;足球运动员的年龄与能力具有怎样的关联&#xff1f; 哪些技能会对足球运…

解析波士顿动力Handle机器人背后的技术(附PPT+视频)

[转] http://www.leiphone.com/news/201703/URrR8CG2tmtghNDl.html 导语&#xff1a;Boston Dynamics 在机器人动力方面堪称翘楚&#xff0c;其由双足或多足机器人组成的机器人天团总是时不时能给我们带来惊喜。上周&#xff0c;Boston Dynamics 又发布了一段视频&#xff0c;并…

解析波士顿Handle机器人背后的技术

在斗鱼直播平台&#xff0c;浙大李超博士的公开课分享了关于handle的技术细节&#xff0c;解析波士顿Handle机器人背后的技术&#xff0c;观察敏锐&#xff0c;思路清晰。 内容介绍了波士顿动力公司的历代4足和2足机器人的开发历史。并结合李超的专业重点讲解了腿的细节。 李…

开源四足机器人 附设计图及代码

斯坦福学生机器人俱乐部&#xff08;Stanford Student Robotics club&#xff09;Extreme Mobility 团队最近迎来了一名新成员——一个名为 Stanford Doggo 的四足机器人。这个机器人能跳 1 米多高&#xff0c;还能表演后空翻。与其他四足机器人动辄上万美元的成本不同&#xf…

ROS2机器人笔记21-03-14

ROS会议和活动&#xff1a; 如果对机器人技术感兴趣&#xff0c;但是看不到任何适合兴趣的项目&#xff0c;那么可能还需要考虑将其应用于同级组织中&#xff0c;例如OpenCV和MoveIt&#xff01; 核心是各种应用和ROS2的逐渐普及 GPU运算越来越主流&#xff0c;Nvidia Jetson…

波斯顿动力机器人“特效专辑”

关注星标公众号&#xff0c;不错过精彩内容 作者 | strongerHuang 公众号 | strongerHuang 波斯顿动力机器人的视频为啥那么火&#xff1f; 首先&#xff0c;他们机器人本身很牛逼&#xff0c;能完成各种高难度动作&#xff0c;拍出来的视频自然很多人关注。 其次&#xff0c;有…

清华2019最新AI发展报告出炉!400页干货,13大领域一文看懂

2019-12-08 20:36:36 当前&#xff0c;人工智能正处在爆发期。我国在人工智能领域的科学技术研究和产业发展起步稍晚&#xff0c;但在最近十余年的时间里抓住了机遇&#xff0c;进入了快速发展阶段。在这个过程中&#xff0c; 技术突破和创造性高端人才对人工智能的发展起着至…

ROS机器人培训基地和暑期学校证书样例

初级工程师证书&#xff08;非高清&#xff09;&#xff1a; 初级师资证书&#xff08;非高清&#xff09;&#xff1a; 为了鼓励学生朋友参与ROS机器人暑期学校&#xff0c;我也参加了并完成了所有任务&#xff0c;证书样本&#xff1a; 2021年&#xff08;非高清&#xff09…

专业的机器人资讯与太空中的ROS

参考&#xff1a;www.therobotreport.com Open Robotics正在与由 Jeff Bezos 创立的亚轨道航天公司Blue Origin和 NASA 合作开发 Space ROS。Space ROS 是 ROS 2 的一个版本&#xff0c;旨在满足航空航天软件在用于任务之前必须满足的验证和确认要求。 机器人操作系统 (ROS) 已…

机器人介绍、应用、前景

机器人介绍、应用、前景 1 介绍1.1 定义1.2 作用1.3 发展历程1.4 分类1.5 三大顾虑1.6 前景 2 种类工业机器臂协作机械臂工业移动机器人复合机器人扫地机器人服务机器人机器狗人形机器人无人机 3 技术3.1 机器人学分类3.2 功能分类3.2.1 感知3.2.2 决策3.2.3 执行AGV减震机构减…

波士顿动力机器人解锁跑酷新技能,凭借感知驱动技术适应技能

编译 | 禾木木 出品 | AI科技大本营&#xff08;ID:rgznai100&#xff09; 波士顿动力开发负责人为大家解读了 Atlas 能够快速越过具有一定倾斜度的障碍物、执行快速转身、跳跃等动作背后的技术。 Atlas 机器人首次完成了复杂的障碍赛。 复杂的障碍赛在波士顿动力公司总部二楼…