OBJUI

uniapp模拟微信聊天功能

2025-08-28 21:34:06 21
<template>
	<view class="chat">
		<scroll-view scroll-y="true" :style="{height: scrollViewHeight + 'px'}" :scroll-top="scrollTop" scroll-with-animation>
		<view class="chat-messages" ref="chatMessages">
			<view class="message-time">今天 10:30</view>
			
			<view v-for="(msg, index) in messages" :key="index" :class="['message', msg.type]">
				<view class="avatar">
					<image src="https://static.jiuxiaoyu.com/v2/chat_avatar.png" style="width:66rpx; height: 66rpx;"></image>
				</view>
				<view class="message-content">
					<view class="message-bubble">{{ msg.content }}</view>
				</view>
			</view>
		</view>
		</scroll-view>
		
		<view class="footer">
			<view class="sound-loading" v-if="chatType ==2 && soundLoading">
				<image  class="ico" src="https://static.jiuxiaoyu.com/v2/sond_loding.gif"></image>
			</view>
			<view class="content">
				<view class="f1">
					<image class="ico" v-if="chatType == 1" src="https://static.jiuxiaoyu.com/v2/chat_ico4.png" @click="onChatType(2)"></image>
					<image class="ico" v-if="chatType == 2" src="https://static.jiuxiaoyu.com/v2/chat_ico1.png" @click="onChatType(1)"></image>
					<input class="uni-input"  v-if="chatType == 1" focus placeholder="请输入内容" v-model="content"/>
					<text class="uni-input say"  v-if="chatType == 2" 
					  @touchstart="startSound()" 
					  @touchend="stopSound()"
					  @touchcancel="soundLoadingHide()">{{soundLoadingTxt}}</text>
					  
					
					<image class="ico" src="https://static.jiuxiaoyu.com/v2/chat_ico2.png" @click="clickEmoji()"></image>
					
					<image class="ico" src="https://static.jiuxiaoyu.com/v2/chat_ico3.png" @click="clickFn()" v-if="!content"></image>
					
					<view class="send-btn" v-if="content" @click="sendMessage()">
						<text>发送</text>
					</view>
				</view>
				
				<!-- 功能区 -->
				<view class="fn-box" v-if="fnshow">
					<image class="ico" src="https://static.jiuxiaoyu.com/v2/chat_ico5_1.png"></image>
					<image class="ico" src="https://static.jiuxiaoyu.com/v2/chat_ico5_2.png"></image>
				</view>
				<!-- 表情区 -->
				<view class="emoji-box" v-if="emojiShow">
					<text class="txt" v-for="(item,index) in emojiList" :key="index" @click="changeEmoji(item)">{{item}}</text>
				</view>
			</view>
			
		</view>
	</view>
</template>
<script>
export default {
	data() {
		return {
			content:'',
			chatType:1, // 1文字,2语音
			fnshow:false,//功能区
			emojiShow:false,
			soundLoading:false,
			soundLoadingTxt:'请按住说话',
			soundTime:0,
			soundTimer:null,
			emojiList:[
				'😀','😁','😅','😂','🙂','😍','😗','😋','😘','😴','😵','😲','😎','🤢','😓','👌','👍','🤝','🙏','🌹','🥀','🍺','🍉'
			],
			messages: [
				{ type: 'other', content: '你好呀!最近在忙什么呢?' },
				{ type: 'self', content: '在学UniApp开发呢,挺有意思的' },
				{ type: 'other', content: 'UniApp确实不错,可以一次开发多端发布' },
				{ type: 'self', content: '是的,我正在做一个聊天界面,类似微信的这种气泡效果' },
				{ type: 'other', content: '看起来效果不错嘛!继续加油!' },
				{ type: 'other', content: '你好呀!最近在忙什么呢?' },
				{ type: 'self', content: '在学UniApp开发呢,挺有意思的' },
				{ type: 'other', content: 'UniApp确实不错,可以一次开发多端发布' },
				{ type: 'self', content: '是的,我正在做一个聊天界面,类似微信的这种气泡效果' },
				{ type: 'other', content: '看起来效果不错嘛!继续加油!' },
				{ type: 'other', content: '你好呀!最近在忙什么呢?' },
				{ type: 'self', content: '在学UniApp开发呢,挺有意思的' },
				{ type: 'other', content: 'UniApp确实不错,可以一次开发多端发布' },
				{ type: 'self', content: '是的,我正在做一个聊天界面,类似微信的这种气泡效果' },
				{ type: 'other', content: '看起来效果不错嘛!继续加油!' }
			],
			
			scrollTop: 0,    // 滚动条位置
			scrollViewHeight: 0, // scroll-view 的可视高度
			contentHeight: 0     // 内容的总高度
		}
	},
	onLoad() {
		
	},
	mounted() {
		const systemInfo = uni.getSystemInfoSync();
		this.scrollViewHeight = systemInfo.windowHeight - uni.upx2px(150); 
		
		this.$nextTick(() => {
			this.scrollToBottom();
		});
	},
	methods:{
		onChatType(type){
			this.emojiShow = false
			this.fnshow = false
			this.chatType = type
		},
		clickFn(){
			this.chatType = 1
			this.fnshow = !this.fnshow
			this.emojiShow = false
		},
		clickEmoji(){
			this.chatType = 1
			this.emojiShow = !this.emojiShow
			this.fnshow = false
		},
		changeEmoji(item){
			this.content = this.content + item
		},
		//开始说话
		startSound(){
			
			this.soundLoading = true
			this.soundLoadingTxt = `松开发送`
			this.soundTime = 0;
			this.soundTimer = setInterval(()=>{
				this.soundTime = this.soundTime + 1
				this.soundLoadingTxt = `松开发送(`+this.soundTime+`秒)`
			},1000)
			
			
		},
		soundLoadingHide(){
			this.soundLoadingTxt = '请按住说话'
			this.soundLoading = false
			clearInterval(this.soundTimer)
			this.soundTime = 0;
		},
		//结束说话
		stopSound(){
			clearInterval(this.soundTimer)
			let soundTime = this.soundTime;
			
			
			this.soundLoadingTxt = '请按住说话'
			this.soundLoading = false
			this.soundTime = 0;
			
			if(soundTime < 5){
				
				return uni.showToast({
					title:'说话时间太短',
					icon:'none'
				})
			}
			return uni.showToast({
				title:'发送成功'
			})
		},
		
		//发送内容
		 sendMessage() {
			 this.emojiShow = false
			if (!this.content.trim()) return;
			
			// 添加新消息
			this.messages.push({
				type: 'self',
				content: this.content
			});
			
			// 清空输入框
			this.content = '';
			
			// 滚动到底部
			this.$nextTick(() => {
				this.scrollToBottom();
			});
			
			
			 // 模拟回复
			setTimeout(() => {
				this.simulateReply();
			}, 1000 + Math.random() * 2000);
		},
		 simulateReply() {
			const replies = [
				'收到你的消息了!',
				'这个功能看起来不错',
				'UniApp确实很方便',
				'继续加油哦!',
				'我明白了',
				'好的,那我们下次再聊'
			];
			
			const reply = replies[Math.floor(Math.random() * replies.length)];
			
			this.messages.push({
				type: 'other',
				content: reply
			});
			
			this.$nextTick(() => {
				this.scrollToBottom();
			});
		},
		 scrollToBottom() {
			 const query = uni.createSelectorQuery().in(this);
			  query.select('.chat-messages').boundingClientRect(data => { 
				if (data) {
				  this.contentHeight = data.height;
				  console.log('data',data)
				  this.scrollTop = this.contentHeight;
				  
				  if (this.scrollTop < 0) {
					this.scrollTop = 0;
				  }
				}
			  }).exec();
		}
	}
}
</script>

<style scoped lang="scss">
	.chat{
		width: 100vw;
		height: 100vh;
		background-color: #f5f5f5;
		box-sizing: border-box;
	
		.chat-messages {
			padding: 20rpx;
			display: flex;
			flex-direction: column;
		}
		
		.message {
			display: flex;
			margin-bottom: 40rpx;
			animation: fadeIn 0.3s;
		}
		
		.message.self {
			flex-direction: row-reverse;
		}
		
		.avatar {
			
			margin: 0 10px;
			
			display: flex;
			align-items: center;
			justify-content: center;
			
		}
		
		.message-content {
			max-width: 65%;
			display: flex;
			flex-direction: column;
			font-size: 26rpx;
		}
		
		.message-bubble {
			padding: 12px 15px;
			border-radius: 5px;
			position: relative;
			line-height: 1.5;
			word-break: break-word;
		}
		
		/* 左侧消息气泡样式 */
		.message.other .message-bubble {
			background-color: #FFF;
			border-top-left-radius: 0;
			box-shadow: 0 2px 5px rgba(0, 0, 0, 0.05);
		}
		
		/* 左侧气泡尖角 */
		.message.other .message-bubble::before {
			content: '';
			position: absolute;
			top: 10px;
			left: -10px;
			width: 0;
			height: 0;
			border-top: 10px solid transparent;
			border-bottom: 0 solid transparent;
			border-right: 10px solid #FFF;
		}
		
		/* 右侧消息气泡样式 */
		.message.self .message-bubble {
			background-color: #95EC69;
			border-top-right-radius: 0;
			box-shadow: 0 2px 5px rgba(0, 0, 0, 0.05);
		}
		
		/* 右侧气泡尖角 */
		.message.self .message-bubble::after {
			content: '';
			position: absolute;
			top: 10px;
			right: -10px;
			width: 0;
			height: 0;
			border-top: 10px solid transparent;
			border-bottom: 0 solid transparent;
			border-left: 10px solid #95EC69;
		}
		
		.message-time {
			font-size: 12px;
			color: #999;
			margin: 5px 0;
			text-align: center;
		}
		
		@keyframes fadeIn {
			from { opacity: 0; transform: translateY(10px); }
			to { opacity: 1; transform: translateY(0); }
		}
		
		/* 滚动条样式 */
		.chat-messages::-webkit-scrollbar {
			width: 5px;
		}
		
		.chat-messages::-webkit-scrollbar-track {
			background: #f1f1f1;
		}
		
		.chat-messages::-webkit-scrollbar-thumb {
			background: #c1c1c1;
			border-radius: 10px;
		}
		
		.chat-messages::-webkit-scrollbar-thumb:hover {
			background: #a8a8a8;
		}
		.footer{
			width: 100%;
			min-height: 110rpx;
			position: fixed;
			left: 0;
			bottom: 0;
			
			.content{
				background: rgba(242, 241, 241, 1);
				min-height: 110rpx;
				width: 100%;
				box-sizing: border-box;
				padding:20rpx;
			}
			.sound-loading{
				width: 100%;
				height: 80rpx;
				box-sizing: border-box;
				background:none;
				display: flex;
				flex-direction: row;
				justify-content: center;
				align-items: center;
				margin-bottom: 20rpx;
				.ico{
					width: 250rpx;
					height: 60rpx;
				}
			}
			.f1{
				width: 100%;
				display: flex;
				flex-direction: row;
				align-items: center;
				justify-content: space-between;
				.ico{
					width: 50rpx;
					height: 50rpx;
					margin:0 10rpx;
				}
				.uni-input{
					box-sizing: border-box;
					margin:0 15rpx;
					flex:1;
					height: 58rpx;
					border-radius: 20rpx;
					background: rgba(249, 249, 249, 1);
					padding-left: 15rpx;
					font-size: 24rpx;
					&.say{
						text-align: center;
						color: #999;
						line-height: 58rpx;
					}
				}
				.send-btn{
					background-color: #07c160;
					color:#fff;
					font-size: 24rpx;
					padding:0 20rpx;
					box-sizing: border-box;
					border-radius: 6rpx;
					height: 50rpx;
					text-align: center;
					line-height: 50rpx;
				}
			}
			.fn-box{
				margin-top: 20rpx;
				border-top: 1px solid #E9E9E9;
				box-sizing: border-box;
				width: 100%;
				padding:30rpx;
				display: flex;
				flex-direction: row;
				flex-wrap: wrap;
				align-items: flex-start;
				justify-content: flex-start;
				.ico{
					width: 90rpx;
					height: 90rpx;
					margin:20rpx;
				}
			}
			.emoji-box{
				margin-top: 20rpx;
				border-top: 1px solid #E9E9E9;
				box-sizing: border-box;
				width: 100%;
				padding:30rpx;
				display: flex;
				flex-direction: row;
				flex-wrap: wrap;
				align-items: flex-start;
				justify-content: flex-start;
				.txt{
					font-size: 42rpx;
					margin:10rpx;
					width: 68rpx;
					height: 68rpx;
				}
			}
		}
	}
</style>
更多精彩,请关注公众号

微信公众号