当前位置:   article > 正文

【易售小程序项目】私聊功能uniapp界面实现 (买家、卖家 沟通商品信息)【后端基于若依管理系统开发】_若依聊天系统

若依聊天系统

效果显示

在这里插入图片描述

WebSocket连接

使用全局变量

本小程序在用户浏览首页的时候创建WebSocket连接,并将连接获得的WebSocket对象存储到全局变量中,方便其他页面来使用WebSocket

首先在项目的main.js文件中声明全局变量socket

在这里插入图片描述

Vue.prototype.$socket = null
  • 1

后面初始化Websocket连接的时候对全局变量进行赋值,赋值完成之后,后续如果需要使用全局变量,直接使用this.$socket即可

Vue.prototype.$socket = this.$socket;
  • 1

WebSocket连接细节

下面的代码中有一个headbeat方法,该方法主要用来定时给WebSocket服务器发送一个信号,告诉WebSocket服务器当前客户端还处于连接状态。当心跳停止的时候(比如客户端断网),后端服务就会将用户信息从连接中移除

/**
* 创建websocket连接
 */
initWebsocket() {
	// console.log("this.socket:" + JSON.stringify(this.$socket))
	// this.$socket == null,刚刚进入首页,还没有建立过websocket连接
	// this.$socket.readyState==0 表示正在连接当中
	// this.$socket.readyState==1 表示处于连接状态
	// this.$socket.readyState==2 表示连接正在关闭
	// this.$socket.readyState==3 表示连接已经关闭
	if (this.$socket == null || (this.$socket.readyState != 1 && this.$socket.readyState != 0)) {
		this.$socket = uni.connectSocket({
			url: "ws://10.23.17.146:8085/websocket/" + uni.getStorageSync("curUser").userName,
			success(res) {
				console.log('WebSocket连接成功', res);
			},
		})
		// console.log("this.socket:" + this.$socket)

		// 监听WebSocket连接打开事件
		this.$socket.onOpen((res) => {
			console.log("websocket连接成功")
			Vue.prototype.$socket = this.$socket;
			// 连接成功,开启心跳
			this.headbeat();
		});
		// 连接异常
		this.$socket.onError((res) => {
			console.log("websocket连接出现异常");
			// 重连
			this.reconnect();
		})
		// 连接断开
		this.$socket.onClose((res) => {
			console.log("websocket连接关闭");
			// 重连
			this.reconnect();
		})
	}
},
/**
 * 重新连接
 */
reconnect() {
	console.log("重连");
	// 防止重复连接
	if (this.lockReconnect == true) {
		return;
	}
	// 锁定,防止重复连接
	this.lockReconnect = true;
	// 间隔一秒再重连,避免后台服务出错时,客户端连接太频繁
	setTimeout(() => {
		this.initWebsocket();
	}, 1000)
	// 连接完成,设置为false
	this.lockReconnect = false;
},
// 开启心跳
headbeat() {
	console.log("websocket心跳");
	var that = this;
	setTimeout(function() {
		if (that.$socket.readyState == 1) {
			// websocket已经连接成功
			that.$socket.send({
				data: JSON.stringify({
					status: "ping"
				})
			})
			// 调用启动下一轮的心跳
			that.headbeat();
		} else {
			// websocket还没有连接成功,重连
			that.reconnect();
		}
	}, that.heartbeatTime);
},
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78

最近和自己聊天的用户信息

界面效果

在这里插入图片描述

界面代码

<template>
	<view class="container">
		<scroll-view @scrolltolower="getMoreChatUserVo">
			<view v-for="(chatUserVo,index) in chatUserVoList" :key="index" @click="trunToChat(chatUserVo)">
				<view style="height: 10px;"></view>
				<view class="chatUserVoItem">
					<view style="display: flex;align-items: center;">
						<uni-badge class="uni-badge-left-margin" :text="chatUserVo.unReadChatNum" absolute="rightTop"
							size="small">
							<u--image :showLoading="true" :src="chatUserVo.userAvatar" width="50px" height="50px"
								:fade="true" duration="450">
								<view slot="error" style="font-size: 24rpx;">加载失败</view>
							</u--image>
						</uni-badge>
					</view>
					<view style="margin: 10rpx;"></view>
					<view
						style="line-height: 20px;width: 100%;display: flex;justify-content: space-between;flex-direction: column;">
						<view style="display: flex;	justify-content: space-between;">
							<view>
								<view class="nickname">{{chatUserVo.userNickname}}
								</view>
								<view class="content">{{chatUserVo.lastChatContent}}</view>
							</view>
							<view class="date">{{formatDateToString(chatUserVo.lastChatDate)}}</view>
						</view>

						<!-- <view style="height: 10px;"></view> -->
						<u-line></u-line>
					</view>
				</view>

			</view>
		</scroll-view>
	</view>
</template>

<script>
	import {
		listChatUserVo
	} from "@/api/market/chat.js";
	import {
		listChat
	} from "@/api/market/chat.js"

	export default {
		data() {
			return {
				chatUserVoList: [],
				page: {
					pageNum: 1,
					pageSize: 15
				},

			}
		},
		created() {

		},
		methods: {
			/**
			 * 滑动到底部,自动加载新一页的数据
			 */
			getMoreChatUserVo() {
				this.page.pageNum++;
				this.listChatUserVo();
			},
			listChatUserVo() {
				listChatUserVo(this.page).then(res => {
					// console.log("res:"+JSON.stringify(res.rows))
					// this.chatUserVoList = res.rows;
					for (var i = 0; i < res.rows.length; i++) {
						this.chatUserVoList.push(res.rows[i]);
					}
				})
			},
			/**
			 * 格式化日期
			 * @param {Object} date
			 */
			formatDateToString(dateStr) {
				let date = new Date(dateStr);
				// 今天的日期
				let curDate = new Date();
				if (date.getFullYear() == curDate.getFullYear() && date.getMonth() == curDate.getMonth() && date
					.getDate() == curDate.getDate()) {
					// 如果和今天的年月日都一样,那就只显示时间
					return this.toDoubleNum(date.getHours()) + ":" + this.toDoubleNum(date.getMinutes());
				} else {
					// 如果年份一样,就只显示月日
					return (curDate.getFullYear() == date.getFullYear() ? "" : (date.getFullYear() + "-")) + this
						.toDoubleNum((
							date
							.getMonth() + 1)) +
						"-" +
						this.toDoubleNum(date.getDate());
				}
			},
			/**
			 * 如果传入的数字是两位数,直接返回;
			 * 否则前面拼接一个0

			 * @param {Object} num
			 */
			toDoubleNum(num) {
				if (num >= 10) {
					return num;
				} else {
					return "0" + num;
				}
			},
			/**
			 * 转到私聊页面
			 */
			trunToChat(chatUserVo) {
				let you = {
					avatar: chatUserVo.userAvatar,
					nickname: chatUserVo.userNickname,
					username: chatUserVo.userName
				}
				uni.navigateTo({
					url: "/pages/chat/chat?you=" + encodeURIComponent(JSON.stringify(you))
				})
			},
			/**
			 * 接收消息
			 */
			receiveMessage() {
				this.$socket.onMessage((response) => {
					// console.log("接收消息:" + response.data);
					let message = JSON.parse(response.data);

					// 收到消息,将未读消息数量加一
					for (var i = 0; i < this.chatUserVoList.length; i++) {
						if (this.chatUserVoList[i].userName == message.from) {
							this.chatUserVoList[i].unReadChatNum++;
							// 显示对方发送的最新消息
							listChat(message.from, {
								pageNum: 1,
								pageSize: 1
							}).then(res => {
								this.chatUserVoList[i].lastChatContent = res.rows[0].content
							});
							break;
						}
					}
				})
			},

		},
		onLoad(e) {
			this.receiveMessage();
		},
		onShow: function() {
			this.chatUserVoList = [];
			this.listChatUserVo();
		},
	}
</script>

<style lang="scss">
	.container {
		padding: 20rpx;

		.chatUserVoItem {
			display: flex;
			margin: 0 5px;

			.nickname {
				font-weight: 700;
			}

			.content {
				color: #A7A7A7;
				font-size: 14px;
				
				/* 让消息只显示1行,超出的文字内容使用...来代替 */
				overflow: hidden;
				text-overflow: ellipsis;
				display: -webkit-box;
				-webkit-line-clamp: 1;
				-webkit-box-orient: vertical;
			}

			.date {
				color: #A7A7A7;
				font-size: 12px;
			}
		}

		// .uni-badge-left-margin {
		// 	margin-left: 10px;
		// }
	}
</style>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195

最近的聊天内容太长

当最近的一条聊天内容太长的时候,页面不太美观,缺少整齐的感觉
在这里插入图片描述
解决的方式非常简单,只需要添加以下样式即可

.content {
	/* 让消息只显示1行,超出的文字内容使用...来代替 */
	overflow: hidden;
	text-overflow: ellipsis;
	display: -webkit-box;
	-webkit-line-clamp: 1;
	-webkit-box-orient: vertical;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

在这里插入图片描述

日期时间显示

本文显示日期时间的时候,遵循以下原则:

  • 如果上次聊天时间的年月日和今天一致,那就只显示时间,即显示 时:分
  • 如果上次聊天时间的年份和今年一致,那就只显示月-日
  • 如果上面的条件都不满足,就显示年-月-日

在显示月、日、时、分的时候,如果数字是一位数字,就在前面补一个零,具体操作如方法toDoubleNum

/**
 * 格式化日期
 * @param {Object} date
 */
formatDateToString(dateStr) {
	let date = new Date(dateStr);
	// 今天的日期
	let curDate = new Date();
	if (date.getFullYear() == curDate.getFullYear() && date.getMonth() == curDate.getMonth() && date
		.getDate() == curDate.getDate()) {
		// 如果和今天的年月日都一样,那就只显示时间
		return this.toDoubleNum(date.getHours()) + ":" + this.toDoubleNum(date.getMinutes());
	} else {
		// 如果年份一样,就只显示月日
		return (curDate.getFullYear() == date.getFullYear() ? "" : (date.getFullYear() + "-")) + this
			.toDoubleNum((
				date
				.getMonth() + 1)) +
			"-" +
			this.toDoubleNum(date.getDate());
	}
},
/**
* 如果传入的数字是两位数,直接返回;
 * 否则前面拼接一个0

 * @param {Object} num
 */
toDoubleNum(num) {
	if (num >= 10) {
		return num;
	} else {
		return "0" + num;
	}
},
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

未读消息数量显示

未读消息数量显示使用角标组件,即uni-badge,使用该组件需要下载安装插件,下载链接,下载之前需要看广告,哈哈哈,当然有钱可以不看

在这里插入图片描述
显示效果如下图
在这里插入图片描述

<uni-badge class="uni-badge-left-margin" :text="chatUserVo.unReadChatNum" absolute="rightTop"
	size="small">
	<u--image :showLoading="true" :src="chatUserVo.userAvatar" width="50px" height="50px"
		:fade="true" duration="450">
		<view slot="error" style="font-size: 24rpx;">加载失败</view>
	</u--image>
</uni-badge>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

私聊界面

界面展示

【微信公众平台模拟的手机界面】
在这里插入图片描述
【手机端,键盘呼出之后的聊天区域】
在这里插入图片描述

代码实现

页面

<template>
	<view style="height:100vh;">
		<!-- @scrolltoupper:上滑到顶部执行事件,此处用来加载历史消息 -->
		<!-- scroll-with-animation="true" 设置滚动条位置的时候使用动画过渡,让动作更加自然 -->
		<scroll-view :scroll-into-view="scrollToView" scroll-y="true" class="messageListScrollView"
			:style="{height:scrollViewHeight}" @scrolltoupper="getHistoryChat()"
			:scroll-with-animation="!isFirstListChat" ref="chatScrollView">
			<view v-for="(message,index) in messageList" :key="message.id" :id="`message`+message.id"
				style="width: 750rpx;min-height: 60px;">
				<view style="height: 10px;"></view>
				<view v-if="message.type==0" class="messageItemLeft">
					<view style="width: 8px;"></view>
					<u--image :showLoading="true" :src="you.avatar" width="50px" height="50px" radius="3"></u--image>
					<view style="width: 7px;"></view>
					<view class="messageContent left">
						{{message.content}}
					</view>
				</view>
				<view v-if="message.type==1" class="messageItemRight">
					<view class="messageContent right">
						{{message.content}}
					</view>
					<view style="width: 7px;"></view>
					<u--image :showLoading="true" :src="me.avatar" width="50px" height="50px" radius="3"></u--image>
					<view style="width: 8px;"></view>
				</view>
			</view>
		</scroll-view>

		<view class="messageSend">
			<view class="messageInput">
				<u--textarea v-model="messageInput" placeholder="请输入消息内容" autoHeight>
				</u--textarea>
			</view>
			<view style="width:5px"></view>
			<view class="commmitButton" @click="send()">发 送</view>
		</view>
	</view>

</template>

<script>
	import {
		getUserProfileVo
	} from "@/api/user";
	import {
		listChat
	} from "@/api/market/chat.js"

	let socket;
	export default {
		data() {
			return {
				webSocketUrl: "",
				socket: null,
				messageInput: '',
				// 我自己的信息
				me: {},
				// 对方信息
				you: {},

				scrollViewHeight: undefined,
				messageList: [],
				// 底部滑动到哪里
				scrollToView: '',
				page: {
					pageNum: 1,
					pageSize: 15
				},
				isFirstListChat: true,
				loadHistory: false,
				// 消息总条数
				total: 0,
			}
		},
		created() {
			this.me = uni.getStorageSync("curUser");
		},
		beforeDestroy() {
			console.log("执行销毁方法");
			this.endChat();
		},
		onLoad(e) {
			// 设置初始高度
			this.scrollViewHeight = `calc(100vh - 20px - 44px)`;
			this.you = JSON.parse(decodeURIComponent(e.you));
			uni.setNavigationBarTitle({
				title: this.you.nickname,
			})
			this.startChat();
			this.listChat();
			this.receiveMessage();
		},

		onReady() {
			// 监听键盘高度变化,以便设置输入框的高度
			uni.onKeyboardHeightChange(res => {
				let keyBoardHeight = res.height;
				console.log("keyBoardHeight:" + keyBoardHeight);
				this.scrollViewHeight = `calc(100vh - 20px - 44px - ${keyBoardHeight}px)`;

				this.scrollToView = '';
				setTimeout(() => {
					this.scrollToView = 'message' + this.messageList[this
						.messageList.length - 1].id;
				}, 150)
			})
		},
		methods: {

			/**
			 * 发送消息
			 */
			send() {
				if (this.messageInput != '') {
					let message = {
						from: this.me.userName,
						to: this.you.username,
						text: this.messageInput
					}
					// console.log("this.socket.send:" + this.$socket)
					// 将组装好的json发送给服务端,由服务端进行转发
					this.$socket.send({
						data: JSON.stringify(message)
					});
					this.total++;
					let newMessage = {
						// code: this.messageList.length,
						type: 1,
						content: this.messageInput
					};
					this.messageList.push(newMessage);
					this.messageInput = '';
					this.toBottom();
				}
			},
			/**
			 * 开始聊天
			 */
			startChat() {
				let message = {
					from: this.me.userName,
					to: this.you.username,
					text: "",
					status: "start"
				}
				// 告诉服务端要开始聊天了
				this.$socket.send({
					data: JSON.stringify(message)
				});
			},
			/**
			 * 结束聊天
			 */
			endChat() {
				let message = {
					from: this.me.userName,
					to: this.you.username,
					text: "",
					status: "end"
				}
				// 告诉服务端要结束聊天了
				this.$socket.send({
					data: JSON.stringify(message)
				});
			},
			/**
			 * 接收消息
			 */
			receiveMessage() {
				this.$socket.onMessage((response) => {
					// console.log("接收消息:" + response.data);
					let message = JSON.parse(response.data);

					let newMessage = {
						// code: this.messageList.length,
						type: 0,
						content: message.text
					};
					this.messageList.push(newMessage);
					this.total++;
					// 让scroll-view自动滚动到最新的数据那里
					// this.$nextTick(() => {
					// 	// 滑动到聊天区域最底部
					// 	this.scrollToView = 'message' + this.messageList[this
					// 		.messageList.length - 1].id;
					// });
					this.toBottom();
				})
			},
			/**
			 * 查询对方和自己最近的聊天数据
			 */
			listChat() {
				return new Promise((resolve, reject) => {
					listChat(this.you.username, this.page).then(res => {
						for (var i = 0; i < res.rows.length; i++) {
							this.total = res.total;
							if (res.rows[i].fromWho == this.me.userName) {
								res.rows[i].type = 1;
							} else {
								res.rows[i].type = 0;
							}
							// 将消息放到数组的首位
							this.messageList.unshift(res.rows[i]);
						}

						if (this.isFirstListChat == true) {
							// this.$nextTick(function() {
							// 	// 滑动到聊天区域最底部
							// 	this.scrollToView = 'message' + this.messageList[this
							// 		.messageList.length - 1].id;
							// })
							this.toBottom();
							this.isFirstListChat = false;
						}
						resolve();
					})

				})

			},
			/**
			 * 滑到最顶端,分页加一,拉取更早的数据
			 */
			getHistoryChat() {
				// console.log("获取历史消息")
				this.loadHistory = true;
				if (this.messageList.length < this.total) {
					// 当目前的消息条数小于消息总量的时候,才去查历史消息
					this.page.pageNum++;
					this.listChat().then(() => {})
				}
			},
			/**
			 * 滑动到聊天区域最底部
			 */
			toBottom() {
				// 让scroll-view自动滚动到最新的数据那里
				this.scrollToView = '';
				setTimeout(() => {
					// 滑动到聊天区域最底部
					this.scrollToView = 'message' + this.messageList[this
						.messageList.length - 1].id;
				}, 150)
			}
		}
	}
</script>

<style lang="scss">
	.messageListScrollView {
		background: #F5F5F5;
		overflow: auto;

		.messageItemLeft {
			display: flex;
			align-items: flex-start;
			justify-content: flex-start;

			.messageContent {
				max-width: calc(750rpx - 10px - 50px - 15px - 10px - 50px - 15px);
				padding: 10px;
				// margin-top: 10px;
				border-radius: 7px;
				font-family: sans-serif;
				// padding: 10px;
				// 让view只包裹文字
				width: auto;
				// display: inline-block !important;
				// display: inline;

				// 解决英文字符串、数字不换行的问题
				word-break: break-all;
				word-wrap: break-word;
			}
		}

		.messageItemRight {
			display: flex;
			align-items: flex-start;
			justify-content: flex-end;

			.messageContent {
				max-width: calc(750rpx - 10px - 50px - 15px - 10px - 50px - 15px);
				padding: 10px;
				// margin-top: 10px;
				border-radius: 7px;
				font-family: sans-serif;
				// padding: 10px;
				// 让view只包裹文字
				width: auto;
				// display: inline-block !important;
				// display: inline;

				// 解决长英文字符串、数字不换行的问题
				word-wrap: break-word;
			}
		}

		.right {
			background-color: #94EA68;
		}

		.left {
			background-color: #ffffff;
		}
	}

	.messageSend {
		display: flex;

		background: #ffffff;
		padding-top: 5px;
		padding-bottom: 15px;

		.messageInput {
			border: 1px #EBEDF0 solid;
			border-radius: 5px;
			width: calc(750rpx - 65px);
			margin-left: 5px;
		}

		.commmitButton {
			height: 38px;
			border-radius: 5px;
			width: 50px;
			display: flex;
			align-items: center;
			justify-content: center;
			color: #ffffff;
			background: #3C9CFF;

		}
	}
</style>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • 282
  • 283
  • 284
  • 285
  • 286
  • 287
  • 288
  • 289
  • 290
  • 291
  • 292
  • 293
  • 294
  • 295
  • 296
  • 297
  • 298
  • 299
  • 300
  • 301
  • 302
  • 303
  • 304
  • 305
  • 306
  • 307
  • 308
  • 309
  • 310
  • 311
  • 312
  • 313
  • 314
  • 315
  • 316
  • 317
  • 318
  • 319
  • 320
  • 321
  • 322
  • 323
  • 324
  • 325
  • 326
  • 327
  • 328
  • 329
  • 330
  • 331
  • 332
  • 333
  • 334
  • 335
  • 336
英文长串不换行问题

在这里插入图片描述

这个问题属于是整串英文被以为是一个单词了,所以没有换行,看下面的句子,英文单词可以比较短的,所以会自动换行
在这里插入图片描述

解决这个问题只需要添加下面的css即可

// 解决长英文字符串、数字不换行的问题
word-wrap: break-word;
  • 1
  • 2

下面是添加之后的效果

在这里插入图片描述

聊天区域自动滑动到底部

在聊天的时候,无论是发送一条新的消息,还是接收到一条新的消息,聊天区域都需要自动滑动到最新的消息那里。本文使用scroll-view组件来包裹显示聊天消息,在scroll-view组件中,可以通过给scroll-into-view属性赋值来指定聊天区域所显示到的位置。使用时需要注意如下问题:

  • 需要先给每一条消息设置一个id属性,id属性存储的内容不能以数字开头,因此本文在id之间拼接了一个字符串’message’
  • scroll-view需要被设置好高度,本文通过绑定一个变量来设置高度,如:style="{height:scrollViewHeight}",因为手机端使用小程序打字时键盘呼出会影响聊天区域的高度

在这里插入图片描述

后续通过给scrollToView设置不同的值即可控制聊天区域的滑动,比如每接收到一条新的消息,就调用toBottom方法,该方法通过设置scrollToView为'message' + this.messageList[this.messageList.length - 1].id将聊天区域滑动到最新的消息处。需要注意的是,在进行该值的设置之前,需要延迟一段时间,否则滑动可能不成功,本文延迟150ms,读者也可以探索不同的值,该值不能太大或者太小。

通过设置scroll-view的属性scroll-with-animation的值为true,可以让消息区域在滑动的时候使用动画过渡,这样滑动更加自然。

键盘呼出,聊天区域收缩,聊天区域滑动到底部

当键盘呼出时,需要将聊天区域的高度减去键盘的高度。同时将scrollToView赋值为最后一条消息的id。需要注意的是,在设置scrollToView之前,需要先将scrollToView设置为空字符串,否则滑动效果可能不成功

onReady() {
	// 监听键盘高度变化,以便设置输入框的高度
	uni.onKeyboardHeightChange(res => {
		let keyBoardHeight = res.height;
		console.log("keyBoardHeight:" + keyBoardHeight);
		this.scrollViewHeight = `calc(100vh - 20px - 44px - ${keyBoardHeight}px)`;

		this.scrollToView = '';
		setTimeout(() => {
			this.scrollToView = 'message' + this.messageList[this
				.messageList.length - 1].id;
		}, 150)
	})
},
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
通知WebSocket服务器哪两个用户开始聊天

为了便于后端在存储聊天数据的时候辨别消息是否为已读状态。比如,在小王开始聊天之前,需要先告诉后端:“小王要开始和小明聊天了”,如果正好小明也告诉后端:“我要和小王聊天了”,那小王发出去的消息就会被设置为已读状态,因为他们两个此时此刻正在同时和对方聊天,那小王发出去的消息就默认被小明看到了,因此设置为已读状态

/**
* 开始聊天
*/
startChat() {
	let message = {
		from: this.me.userName,
		to: this.you.username,
		text: "",
		status: "start"
	}
	// 告诉服务端要开始聊天了
	this.$socket.send({
		data: JSON.stringify(message)
	});
},
/**
 * 结束聊天
 */
endChat() {
	let message = {
		from: this.me.userName,
		to: this.you.username,
		text: "",
		status: "end"
	}
	// 告诉服务端要结束聊天了
	this.$socket.send({
		data: JSON.stringify(message)
	});
},
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

请求工具

[chat.js]

import httpRequest from '@/utils/request'

// 查询聊天数据列表
export function list(query) {
	return httpRequest.request({
		url: '/market/chat/list',
		method: 'get',
		params: query
	})
}

// 查询最近和自己聊天的用户
export function listChatUserVo(page) {
	return httpRequest.request({
		url: `/market/chat/listChatUserVo?pageNum=${page.pageNum}&pageSize=${page.pageSize}`,
		method: 'get'
	})
}

// 查询用户和自己最近的聊天信息
export function listChat(toUsername, page) {
	return httpRequest.request({
		url: `/market/chat/listChat/${toUsername}?pageNum=${page.pageNum}&pageSize=${page.pageSize}`,
		method: 'get'
	})
}


// 查询聊天数据详细
export function getChat(id) {
	return httpRequest.request({
		url: '/market/chat/getInfo/' + id,
		method: 'get'
	})
}

// 新增聊天数据
export function addChat(data) {
	return httpRequest.request({
		url: '/market/chat',
		method: 'post',
		data: data
	})
}

// 修改聊天数据
export function updateChat(data) {
	return httpRequest.request({
		url: '/market/chat',
		method: 'put',
		data: data
	})
}

// 删除聊天数据
export function delChat(id) {
	return httpRequest.request({
		url: '/market/chat/' + id,
		method: 'delete'
	})
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61

[user.js]

import httpRequest from '@/utils/request'

// 查询用户列表
export function listUser(query) {
	return httpRequest.request({
		url: '/system/user/list',
		method: 'get',
		params: query
	})
}

// 新增用户
export function addUser(data) {
	return httpRequest.request({
		url: '/system/user',
		method: 'post',
		data: data
	})
}

// 修改用户
export function updateUser(data) {
	return httpRequest.request({
		url: '/system/user',
		method: 'put',
		data: data
	})
}

// 删除用户
export function delUser(userId) {
	return httpRequest.request({
		url: '/system/user/' + userId,
		method: 'delete'
	})
}

// 用户密码重置
export function resetUserPwd(userId, password) {
	const data = {
		userId,
		password
	}
	return httpRequest.request({
		url: '/system/user/resetPwd',
		method: 'put',
		data: data
	})
}

// 用户状态修改
export function changeUserStatus(userId, status) {
	const data = {
		userId,
		status
	}
	return httpRequest.request({
		url: '/system/user/changeStatus',
		method: 'put',
		data: data
	})
}

// 查询用户个人信息
export function getUserProfile() {
	return httpRequest.request({
		url: '/system/user/profile',
		method: 'get'
	})
}

// 查询用户个人信息Vo
export function getUserProfileVo() {
	return httpRequest.request({
		url: '/system/user/profile/vo',
		method: 'get'
	})
}

// 修改用户个人信息
export function updateUserProfile(data) {
	return httpRequest.request({
		url: '/system/user/profile',
		method: 'put',
		data: data
	})
}

// 用户密码重置
export function updateUserPwd(oldPassword, newPassword) {
	const data = {
		oldPassword,
		newPassword
	}
	return httpRequest.request({
		url: '/system/user/profile/updatePwd',
		method: 'put',
		data: data
	})
}

// 用户头像上传
export function uploadAvatar(data) {
	return httpRequest.request({
		url: '/system/user/profile/avatar',
		method: 'post',
		data: data
	})
}

// 查询授权角色
export function getAuthRole(userId) {
	return httpRequest.request({
		url: '/system/user/authRole/' + userId,
		method: 'get'
	})
}

// 保存授权角色
export function updateAuthRole(data) {
	return httpRequest.request({
		url: '/system/user/authRole',
		method: 'put',
		params: data
	})
}

// 查询部门下拉树结构
export function deptTreeSelect() {
	return httpRequest.request({
		url: '/system/user/deptTree',
		method: 'get'
	})
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134

同项目其他文章

该项目的其他文章请查看【易售小程序项目】项目介绍、小程序页面展示与系列文章集合

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/AllinToyou/article/detail/698528
推荐阅读
相关标签
  

闽ICP备14008679号