前言
最近刚好在做这方面的功能,就网上看了下,发现很多种写法,但是有些写的很乱,我也看的很麻烦,干脆就自己写一个简单的静态版本放在这,以后需要用到的时候可以直接拿着改改就能用。后面我还会继续更新有交互逻辑的模拟聊天室,包括pc端的聊天室也会写,这里就先从移动端的微信聊天窗口开始。
效果图
1,这个是聊天窗的大体样式布局,我们通过flex布局让他分左右,其实思路也简单,就是后期后端返回的数据中有一个字段是左右,后端判断是你自己的信息就右边,别人发的信息就左边。我们拿到字段调用对应不同的样式就行了。
2,这里是输入框,当他输入到超过一定高度的时候就会出现滚动条。这里可以调整,是vant的组件。
注意事项
因为我这里用了百分比的高度。这样可以自适应好一点。
但有可能会出现一个问题,就是你使用这个代码,发现高度不对。
那你需要看一下你的html和body有没有设置高百分百,不然这里无法继承到。
如何设置呢,就是直接去app.vue页面,然后这样,把html,body和#app都设置一下百分百高度就可以了
<style>
* {
padding: 0;
margin: 0;
}
html,
body,
#app {
height: 100%;
width: 100%;
}
</style>
这里用了一些vant框架的组件,所以如果没有下载的安装一下vant就可以了
这是vue2版本的
npm i vant@latest-v2 -S
然后main.js引入
import Vue from 'vue';
import Vant from 'vant';
import 'vant/lib/index.css';
Vue.use(Vant);
代码部分
<template>
<div class="wrap">
<div class="title">
<div>
<van-icon
name="arrow-left"
size="20"
style="margin-left: 10px"
@click="onClickLeft"
/>
</div>
<div>{
{
userName }}</div>
<div>
<van-icon
name="ellipsis"
size="22"
style="margin-right: 10px"
@click="onClickRight"
/>
</div>
</div>
<div class="content_box" id="box" ref="scrollBox">
<div class="timer">2022-08-02 11:08:07</div>
<div
:class="item.position == 'left' ? 'userbox2' : 'userbox'"
v-for="(item, index) in chatList"
:key="index"
>
<div :class="item.position == 'left' ? 'nameInfo2' : 'nameInfo'">
<div style="font-size: 14px">{
{
item.username }}</div>
<div
:class="item.position == 'left' ? 'contentText2' : 'contentText'"
>
{
{
item.content }}
</div>
</div>
<div>
<van-image round width="50px" height="50px" :src="item.url" />
</div>
</div>
</div>
<div class="bottom">
<van-field
v-model="inputValue"
center
type="textarea"
:autosize="{ maxHeight: 100, minHeight: 25 }"
placeholder="请输入内容"
rows="1"
>
<template #button>
<van-button size="small" type="primary" @click="sendOut">发送</van-button>
</template>
</van-field>
</div>
</div>
</template>
<script>
export default {
data() {
return {
//聊天数据
chatList: [
{
url: "https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg",
username: "张三",
content: "模拟数据123模拟数据123模拟数据123模拟数据123",
position: "left",
},
{
url: "https://img01.yzcdn.cn/vant/cat.jpeg",
username: "李四",
content: "模拟数据123模拟数据123模拟数据123模拟数据123模拟数据123",
position: "right",
},
{
url: "https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg",
username: "张三",
content: "模拟数据123",
position: "left",
},
{
url: "https://img01.yzcdn.cn/vant/cat.jpeg",
username: "李四",
content: "模拟数据123模拟数据",
position: "right",
},
{
url: "https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg",
username: "张三",
content: "模拟数据123",
position: "left",
},
{
url: "https://img01.yzcdn.cn/vant/cat.jpeg",
username: "李四",
content: "模拟数据123模拟数据",
position: "right",
},
{
url: "https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg",
username: "张三",
content: "模拟数据123",
position: "left",
},
{
url: "https://img01.yzcdn.cn/vant/cat.jpeg",
username: "李四",
content: "模拟数据123模拟数据",
position: "right",
},
{
url: "https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg",
username: "张三",
content: "模拟数据123",
position: "left",
},
{
url: "https://img01.yzcdn.cn/vant/cat.jpeg",
username: "李四",
content: "模拟数据123模拟数据",
position: "right",
},
{
url: "https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg",
username: "张三",
content: "模拟数据123",
position: "left",
},
{
url: "https://img01.yzcdn.cn/vant/cat.jpeg",
username: "李四",
content: "模拟数据123模拟数据",
position: "right",
},
{
url: "https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg",
username: "张三",
content: "模拟数据123",
position: "left",
},
{
url: "https://img01.yzcdn.cn/vant/cat.jpeg",
username: "李四",
content: "模拟数据123模拟数据",
position: "right",
},
],
//用户名
userName: "张三",
//输入内容
inputValue: "",
//滚动条距离顶部距离
scrollTop:0
};
},
mounted(){
this.setPageScrollTo()
//创建监听内容部分滚动条滚动
this.$refs.scrollBox.addEventListener('scroll',this.srTop)
},
methods: {
//返回
onClickLeft() {
console.log("返回");
},
//更多
onClickRight() {
console.log("按钮");
},
//滚动条默认滚动到最底部
setPageScrollTo(s, c) {
//获取中间内容盒子的可见区域高度
this.scrollTop = document.querySelector("#box").offsetHeight;
setTimeout((res) => {
//加个定时器,防止上面高度没获取到,再获取一遍。
if (this.scrollTop != this.$refs.scrollBox.offsetHeight) {
this.scrollTop = document.querySelector("#box").offsetHeight;
}
}, 100);
//scrollTop:滚动条距离顶部的距离。
//把上面获取到的高度座位距离,把滚动条顶到最底部
this.$refs.scrollBox.scrollTop = this.scrollTop;
},
//滚动条到达顶部
srTop(){
//判断:当滚动条距离顶部为0时代表滚动到顶部了
if(this.$refs.scrollBox.scrollTop==0){
//逻辑简介:
//到顶部后请求后端的方法,获取第二页的聊天记录,然后插入到现在的聊天数据前面。
//如何插入前面:可以先把获取的数据保存在 A 变量内,然后 this.chatList=A.concat(this.chatList)把数组合并进来就可以了
//拿聊天记录逻辑:
//第一次调用一个请求拉历史聊天记录,发请求时参数带上页数 1 传过去,拿到的就是第一页的聊天记录,比如一次拿20条。你显示出来
//然后向上滚动到顶部时,触发新的请求,在请求中把分页数先 +1 然后再请求,这就拿到了第二页数据,然后通过concat合并数组插入进前面,依次类推,功能完成!
console.log('到顶了,滚动条位置 :',this.$refs.scrollBox.scrollTop);
}
},
sendOut(){
console.log('发送成功');
}
},
};
</script>
<style scoped>
.wrap {
height: 100%;
width: 100%;
position: relative;
}
.title {
height: 40px;
width: 100%;
background-color: #eaeaea;
display: flex;
justify-content: space-between;
align-items: center;
}
.bottom {
min-height: 50px;
width: 100%;
border-top: 1px solid #eaeaea;
position: fixed;
bottom: 0;
}
.content_box {
/*
中间栏计算高度,110是包含了上下固定的两个元素高度90
这里padding:10px造成的上下够加了10,把盒子撑大了,所以一共是20要减掉
然后不知道是边框还是组件的原因,导致多出了一些,这里再减去5px刚好。不然会出现滚动条到顶或者底部的时候再滚动的话就会报一个错,或者出现滚动条变长一下的bug
*/
height: calc(100% - 115px);
overflow: auto;
padding: 10px;
}
.timer {
text-align: center;
color: #c2c2c2;
}
/* 发送的信息样式 */
/*
右边消息思路解释:首先大盒子userbox内放两个盒子,一个放头像,一个放用户名和发送的内容,我们先用flex让他横向排列。
然后把写文字的大盒子设置flex:1。这个属性的意思就是让这个元素撑满父盒子剩余位置。然后我们再把文字盒子设置flex,并把他对齐方式设置为尾部对齐就完成了基本的结构,然后微调一下就可以了
*/
.userbox {
width: 100%;
display: flex;
margin-bottom: 10px;
}
.nameInfo {
/* 用flex:1把盒子撑开 */
flex: 1;
margin-right: 10px;
/* 用align-items把元素靠右对齐 */
display: flex;
flex-direction: column;
align-items: flex-end;
}
.contentText {
background-color: #9eea6a;
/* 把内容部分改为行内块元素,因为盒子flex:1把盒子撑大了,所以用行内块元素让内容宽度不根据父盒子来 */
display: inline-block;
/* 这四句是圆角 */
border-top-left-radius: 10px;
border-top-right-radius: 0px;
border-bottom-right-radius: 10px;
border-bottom-left-radius: 10px;
/* 最大宽度限定内容输入到百分61换行 */
max-width: 61%;
padding: 5px 10px;
/* 忽略多余的空白,只保留一个空白 */
white-space: normal;
/* 换行显示全部字符 */
word-break: break-all;
margin-top: 3px;
font-size: 14px;
}
/* 接收的信息样式 */
/*
左边消息思路解释:跟上面一样,就是换一下位置,首先通过把最外层大盒子的排列方式通过flex-direction: row-reverse;属性翻转,也就是头像和文字盒子换位置
然后删除掉尾部对齐方式,因为不写这个默认是左对齐的。我们写的左边就没必要再写了。
*/
.userbox2 {
width: 100%;
display: flex;
flex-direction: row-reverse;
margin-bottom: 10px;
}
.nameInfo2 {
/* 用flex:1把盒子撑开 */
flex: 1;
margin-left: 10px;
}
.contentText2 {
background-color: #9eea6a;
/* 把内容部分改为行内块元素,因为盒子flex:1把盒子撑大了,所以用行内块元素让内容宽度不根据父盒子来 */
display: inline-block;
/* 这四句是圆角 */
border-top-left-radius: 0px;
border-top-right-radius: 10px;
border-bottom-right-radius: 10px;
border-bottom-left-radius: 10px;
/* 最大宽度限定内容输入到百分61换行 */
max-width: 61%;
padding: 5px 10px;
/* 忽略多余的空白,只保留一个空白 */
white-space: normal;
/* 换行显示全部字符 */
word-break: break-all;
margin-top: 3px;
font-size: 14px;
}
</style>