当前位置: 首页 > news >正文

用uniapp 及socket.io做一个简单聊天app 撤回聊天及保留聊天记录 6

撤回的设计

通过聊天,发送一个信息,界面自动将信息撤回,
当时要有时间的限制。同时也要将撤回记录到数据库中。
	async sendMessage(message, type = 'text') {this.$refs.popup.close();const messageData = {sn: uuidv4(),group_name: this.groupName,avatar: this.user.avatar_url,content: message,user_name: this.user.username,type: type,fid: this.user.id,tid: this.tid,created_at: this.getCurrentTimeToMinute(),receiver_type: this.receiver_type};this.socket.emit('sendMessage', messageData);this.inputValue = '';const token = uni.getStorageSync('token');try {const [error, response] = await uni.request({url: `${config.apiBaseUrl}/addmessage`,method: 'POST',header: {Authorization: `Bearer ${token}`},data: messageData});if (error) {throw new Error(`Request failed with error: ${error}`);}} catch (error) {}this.$nextTick(() => {this.setScrollTop();});},

以上是发送信息到websocket, 又将信息写入到数据库。我们用了一个sn,让记录保执唯一,以利于我们撤消。所以也要要求我们收到的信息也有这一个sn.

		this.socket.on('message', (msg) => {if (msg.type == 'broadcast') {return;}let msgs = {sn:msg.sn,avatar: msg.avatar,isMe: msg.fid == this.user.id ? true : false,content: msg.content,type: msg.type,sn: msg.sn,createat: Math.floor(Date.now() / 1000),time: Date.now(),withdraw:0,};this.list.push(msgs);this.setScrollTop();});

注意这时我们加了一个time 为的是进行撤消判断。我们假设5分钟内可以撤消。withdraw表是撤消否,表示没有撤。重写以上代码:

	this.socket.on('message', (msg) => {if (msg.type == 'broadcast') {return;}if (msg.type == 'widthdraw') {//查出 msg.sn 将此记录信息改为撤回this.list.forEach((item, index) => {if (item.sn == msg.content) {this.list[index].content = '[消息已撤回]';this.list[index].type = 'text';this.list[index].withdraw = 1;}});return;}let msgs = {sn:msg.sn,avatar: msg.avatar,isMe: msg.fid == this.user.id ? true : false,content: msg.content,type: msg.type,sn: msg.sn,createat: Math.floor(Date.now() / 1000),time: Date.now(),withdraw:0,};this.list.push(msgs);this.setScrollTop();});},

当然搪消时,我们要写一个方向法:

	withdraw(item) {let _=this;const currentTime = Date.now();const messageTime = parseInt(item.time);const oneMinute = config.minute; // 60 * 1000 millisecondsif (currentTime <( messageTime + oneMinute)) {uni.showModal({title: '提示',content: '确认删除该条信息吗?',success: function (res) {if (res.confirm) {// 执行确认后的操作if(_.canwithdraw(item)){const messageData = {sn: uuidv4(),group_name:  _.groupName,avatar:  _.user.avatar_url,content: item.sn,user_name:  _.user.username,type: 'widthdraw',fid:  _.user.id,tid:  _.tid,created_at:  _.getCurrentTimeToMinute(),receiver_type:  _.receiver_type};_.socket.emit('sendMessage', messageData);}else{uni.showToast({title: '超过一分钟不能撤回',icon: 'none'});}} else {// 执行取消后的操作}}});}},canwithdraw(item){const currentTime = Date.now();const messageTime = parseInt(item.time);const oneMinute = config.minute; // 60 * 1000 millisecondsif (currentTime > (messageTime + oneMinute)) {return false;}else{return true;}},

canwithdraw是判断是不是可以取消聊天。这里我们改一下聊天窗口,将撤消加进去:

<template><view class=""><scroll-viewscroll-y="true"class="scroll-box":style="{ height: windowObj.windowHeight - windowObj.statusBarHeight - 94 + 'px' }":scroll-top="scrollHeight"@scrolltoupper="loadMores"><view class="scroll-view"><view class="news-box" v-for="(item, index) in list" :key="index"><view class="message-type" v-if="['left', 'join', 'kick'].includes(item.type)">{{ item.content }}</view><imageclass="avatar":class="[item.isMe ? 'is-me' : 'avatar-right']":src="item.avatar"mode="aspectFill"v-if="item.type != 'kick' && item.type != 'join' && item.type != 'left'"></image><view class="message-box" :class="{ 'is-me': item.isMe }" v-if="item.type != 'kick' && item.type != 'join' && item.type != 'left'"><text class="message" v-if="item.type == 'text'">{{ item.content || '' }}<image src="../../static/withdraw.png" style="width: 50rpx; height: 50rpx" mode="aspectFill" v-if="item.isMe&&canwithdraw(item)&&item.withdraw==0" @tap="withdraw(item)"></image></text><text class="message_img" v-if="item.type == 'image'"><image class="message-image" :src="item.content" mode="aspectFill" @click="playVoice(item.content)" /><image src="../../static/withdraw.png" style="width: 50rpx; height: 50rpx" mode="aspectFill" v-if="item.isMe&&canwithdraw(item)&&item.withdraw==0" @tap="withdraw(item)"></image></text><text class="message_img" v-if="item.type == 'video'"><video v-if="item.content" :src="item.content" controls></video><image src="../../static/withdraw.png" style="width: 50rpx; height: 50rpx" mode="aspectFill" v-if="item.isMe&&canwithdraw(item)&&item.withdraw==0"  @tap="withdraw(item)"></image></text><text class="message_img" v-if="item.type == 'audio'"><image class="message-image" src="../../static/chat/play.png" mode="aspectFill" /><image src="../../static/withdraw.png" style="width: 50rpx; height: 50rpx" mode="aspectFill" v-if="item.isMe&&canwithdraw(item)&&item.withdraw==0"  @tap="withdraw(item)"></image></text></view></view></view></scroll-view><view class="base-btn" :class="{ 'base-btn-popup-open': isPopupOpen || isPopupAudioOpen }"><view class="base-con unify-flex"><view @tap="more"><image src="../../static/chat/more.png" style="width: 50rpx; height: 50rpx"></image></view><input class="input-text" type="text" :value="inputValue" placeholder="说些什么吧" @input="getInput" @confirm="tapTo(2)" /><view @click="tapTo(2)"><image src="../../static/chat/chat.png" style="width: 50rpx; height: 50rpx"></image></view></view></view><uni-popup ref="popup" type="bottom" :style="{ height: '200rpx' }" @change="onPopupChange"><view :style="{ width: '100%', backgroundColor: '#fff', height: '200rpx', overflowY: 'scroll' }" class="popup-content"><view class="popup-items"><view class="popup-item" v-if="type == 'group'"  @tap="adduserTogroup"><image src="../../static/chat/add.png" style="width: 50rpx; height: 50rpx"></image><text>添加</text></view><view class="popup-item" @click="chooseFile"><image src="../../static/chat/pic.png" style="width: 50rpx; height: 50rpx"></image><text>图片</text></view><view class="popup-item" @tap="audio"><image src="../../static/chat/audio.png" style="width: 50rpx; height: 50rpx"></image><text>音频</text></view><view class="popup-item" @tap="openCamera"><image src="../../static/chat/video.png" style="width: 50rpx; height: 50rpx"></image><text>视频</text></view><view class="popup-item"><image src="../../static/chat/black.png" style="width: 50rpx; height: 50rpx"></image><text>拉黑</text></view><view class="popup-item" v-if="type == 'group'"><image src="../../static/chat/exit-group.png" style="width: 50rpx; height: 50rpx"></image><text>退群</text></view></view></view></uni-popup><uni-popup ref="popupAudio" type="bottom" :style="{ height: '200rpx' }" @change="onPopupAudioChange"><view :style="{ width: '100%', backgroundColor: '#fff', height: '200rpx', overflowY: 'scroll' }" class="popup-content"><view class="popup-item" @click="startRecording"><image src="../../static/chat/beginaudio.png" style="width: 50rpx; height: 50rpx"></image><text>录音</text></view><view class="popup-item" @click="stopRecording"><image src="../../static/chat/stop.png" style="width: 50rpx; height: 50rpx"></image><text>停止</text></view><view class="popup-item" @tap="playRecording"><image src="../../static/chat/play.png" style="width: 50rpx; height: 50rpx"></image><text>播放</text></view><view class="popup-item" @tap="upsong"><image src="../../static/chat/send.png" style="width: 50rpx; height: 50rpx"></image><text>发送</text></view><view class="popup-item" @tap="exitchat"><image src="../../static/chat/exit.png" style="width: 50rpx; height: 50rpx"></image><text>退出</text></view></view></uni-popup></view>
</template>

下面我们还要写一个接口,将撤消记录下来:

app.get('/withdraw', authenticateToken, async (req, res) => {try {const userId = req.user.id;let { sn } = req.query;await Message.update({ is_retracted:1,retracted_at:Date.now() }, { where: { sn:sn } });return res.json({ code:0, message: '撤消成功' });} catch (error) {return res.json({ code:1, message: '获取用户信息时出错' });}
});

接口这里记录。

	if (msg.type == 'widthdraw') {//查出 msg.sn 将此记录信息改为撤回console.log(msg);this.list.forEach((item, index) => {if (item.sn == msg.content) {this.list[index].content = '[消息已撤回]';this.list[index].type = 'text';this.list[index].withdraw = 1;this.widthdrawRow(item.sn)}});return;}async widthdrawRow(sn) {const token = uni.getStorageSync('token');if (!token) return;try {const [error, response] = await uni.request({url: `${config.apiBaseUrl}/withdraw`,method: 'GET',header: {Authorization: `Bearer ${token}`},data: {sn: sn}});if (error) {throw new Error(`Request failed with error: ${error}`);}if (response.data.code === 0) {return true;} else {return false;}} catch (error) {return false;}},

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 基于RHEL7的服务器批量安装
  • Vue前端面试基础(一)
  • ptrade排坑笔记——执行k_start.sh脚本发现没有生成日志?
  • html+css+js+jquery实现一个 飘零的树叶
  • 将元组类型的日期时间转换为字符串格式time.asctime([t])
  • RuoYi-Vue源码阅读(一):验证码模块
  • Kylin系列(二)使用
  • 缓存常见问题优化
  • 树莓派边缘计算网关搭建:集成MQTT、SQLite与Flask的完整解决方案
  • 数据结构初阶最终讲:排序
  • 使用python-pptx代码添加幻灯片:向PPT中插入新的幻灯片页面
  • Openwrt配置ZeroTier,实现公网访问内网中服务器
  • Windows下,C# 通过FastDDS高效通信
  • 碳化硅陶瓷膜过滤设备优异的过滤性能
  • 前端技术 -- 动画效果之GSAP作用与使用示例
  • ----------
  • 10个确保微服务与容器安全的最佳实践
  • ES学习笔记(12)--Symbol
  • Java Agent 学习笔记
  • MySQL-事务管理(基础)
  • OpenStack安装流程(juno版)- 添加网络服务(neutron)- controller节点
  • PAT A1050
  • Quartz实现数据同步 | 从0开始构建SpringCloud微服务(3)
  • React中的“虫洞”——Context
  • Spring Cloud中负载均衡器概览
  • Vue.js源码(2):初探List Rendering
  • vue学习系列(二)vue-cli
  • Webpack 4 学习01(基础配置)
  • 从零开始的无人驾驶 1
  • 警报:线上事故之CountDownLatch的威力
  • 漂亮刷新控件-iOS
  • 前端相关框架总和
  • media数据库操作,可以进行增删改查,实现回收站,隐私照片功能 SharedPreferences存储地址:
  • postgresql行列转换函数
  • ​Z时代时尚SUV新宠:起亚赛图斯值不值得年轻人买?
  • ​无人机石油管道巡检方案新亮点:灵活准确又高效
  • #include<初见C语言之指针(5)>
  • #nginx配置案例
  • $(this) 和 this 关键字在 jQuery 中有何不同?
  • $.ajax()参数及用法
  • (16)UiBot:智能化软件机器人(以头歌抓取课程数据为例)
  • (a /b)*c的值
  • (day6) 319. 灯泡开关
  • (补充):java各种进制、原码、反码、补码和文本、图像、音频在计算机中的存储方式
  • (亲测有效)解决windows11无法使用1500000波特率的问题
  • (三)终结任务
  • .bat批处理(三):变量声明、设置、拼接、截取
  • .NET Framework 4.6.2改进了WPF和安全性
  • .Net Web项目创建比较不错的参考文章
  • /tmp目录下出现system-private文件夹解决方法
  • ::
  • @RequestMapping 和 @GetMapping等子注解的区别及其用法
  • [2019/05/17]解决springboot测试List接口时JSON传参异常
  • [AR Foundation] 人脸检测的流程
  • [BJDCTF 2020]easy_md5