<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>
更多精彩,请关注公众号

微信公众号