撤回的设计

通过聊天,发送一个信息,界面自动将信息撤回,
当时要有时间的限制。同时也要将撤回记录到数据库中。
	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 milliseconds
			
			  if (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 milliseconds
					
			 if (currentTime > (messageTime + oneMinute)) {
				 return false;
			 }else{
				 return true;
			 }

		},
		

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

<template>
	<view class="">
		<scroll-view
			scroll-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>
					<image
						class="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;
			}
			
		},

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

08-07 01:41