效果展示
由于一些其他的原因,只做了聊天的功能,并且助手是写死的,这个再移动端上测试是没有问题的
接下来上代码:
聊天页面的实现代码
这里面了使用了自定义组件封装
主要的实现原理是,在<script>
标签中嵌入了方法先捕捉消息列表滚动的对象, 我这个页面捕捉到是<body>
标签 所以 我就直接使用了 window.scrollTo(0, this.$refs.list.scrollHeight);
来将整个页面进行了滚动,滚动的高度就是聊天内容边框的高度,然后将这个方法设置每次消息发送之后进行滚动,对方消息过来的时候同样执行这个方法进行滚动
this.$nextTick(() => {
// 一定要用nextTick
// console.log(this.$refs.list.scrollHeight);
window.scrollTo(0, this.$refs.list.scrollHeight);
});
注:这里的Title 以及下面消息展示,都是使用了自定的组件,代码我会贴在最下面,仅供大家交流学习。
<template>
<div class="Serve" :style="{ height: curHeight + 'px' }">
<!-- 顶部标签栏 -->
<div class="title_warp">
<Title title="我的客服" :back="true"></Title>
</div>
<div class="serve_main" ref="list">
<div
class="message_warp"
v-for="(item, index) in messageList"
:key="index"
>
<!-- 客服信息 -->
<other-chat
v-if="item.user == 'other'"
:time="item.time"
:avatar="item.avatar"
:con="item.con"
></other-chat>
<!-- 我的信息 -->
<my-chat
v-else
:time="item.time"
:avatar="item.avatar"
:con="item.con"
></my-chat>
</div>
</div>
<div class="inp_warp">
<input
class="inp"
@focus="foucs"
v-model="sendVal"
v-on:keyup.enter="send"
type="text"
placeholder="请输入内容..."
/>
<van-icon class="icon_inp" size="24px" color="#9a9a9a" name="smile-o" />
<van-icon class="icon_inp" size="24px" color="#9a9a9a" name="add-o" />
</div>
</div>
</template>
<script>
function findScroller(element) {
element.onscroll = function () {
// console.log(element);
};
Array.from(element.children).forEach(findScroller);
}
findScroller(document.body);
import Title from "../../components/Title";
import OtherChat from "../../components/OtherChat";
import MyChat from "../../components/MyChat";
import { mapState } from "vuex";
import { formatDate } from "../../utils/date";
export default {
name: "Serve",
components: {
Title,
OtherChat,
MyChat,
},
data() {
return {
// 屏幕高度
curHeight: 0,
// 信息体
messageList: [
{
avatar: "https://bucket-ans.oss-cn-hangzhou.aliyuncs.com/2.jpg",
time: formatDate(new Date(), "yyyy.mm.dd hh:mi"),
con: "您好,欢迎来带我的客服中心,您的专属客服小浴为您服务!",
user: "other",
},
{
avatar: "https://bucket-ans.oss-cn-hangzhou.aliyuncs.com/2.jpg",
time: formatDate(new Date(), "yyyy.mm.dd hh:mi"),
con: "我们致力于打造一个为老人服务至上的平台!",
user: "other",
},
{
avatar: "https://bucket-ans.oss-cn-hangzhou.aliyuncs.com/2.jpg",
time: formatDate(new Date(), "yyyy.mm.dd hh:mi"),
con: "如有疑问请致电 109-3301-4401!",
user: "other",
},
{
avatar: "https://bucket-ans.oss-cn-hangzhou.aliyuncs.com/2.jpg",
time: formatDate(new Date(), "yyyy.mm.dd hh:mi"),
con: "小浴随时在线为您解答疑惑,请问您有什么需要帮助的嘛?",
user: "other",
},
{
avatar: "https://bucket-ans.oss-cn-hangzhou.aliyuncs.com/2.jpg",
time: formatDate(new Date(), "yyyy.mm.dd hh:mi"),
con: "小浴随时在线为您解答疑惑,举报电话请联系:109-4401-3301",
user: "other",
},
],
// 输入框
sendVal: "",
};
},
computed: {
...mapState(["userInfo"]),
},
created() {
this.initMessage();
this.beforeMount(0);
},
methods: {
initMessage() {
var other = {
avatar: "https://bucket-ans.oss-cn-hangzhou.aliyuncs.com/2.jpg",
time: formatDate(new Date(), "yyyy.mm.dd hh:mi"),
con: "您好,欢迎来带我的客服中心,您的专属客服小浴为您服务,如有疑问请致电官方:109-4431-2231,小浴可能还不够聪明,如果有什么冒犯的地方请您理解!",
user: "other",
};
setTimeout(() => {
this.messageList.push(other);
}, 1000);
},
// 发送消息
send() {
var item = {
avatar: this.userInfo.avatar,
time: formatDate(new Date(), "yyyy.mm.dd hh:mi"),
con: this.sendVal,
user: "my",
};
this.messageList.push(item);
this.$nextTick(() => {
// 一定要用nextTick
// console.log(this.$refs.list.scrollHeight);
window.scrollTo(0, this.$refs.list.scrollHeight);
});
this.sendVal = "";
var other = {
avatar: "https://bucket-ans.oss-cn-hangzhou.aliyuncs.com/2.jpg",
time: formatDate(new Date(), "yyyy.mm.dd hh:mi"),
con: "很抱歉,我没听懂您在说什么",
user: "other",
};
setTimeout(() => {
this.messageList.push(other);
this.$nextTick(() => {
// 一定要用nextTick
// console.log(this.$refs.list.scrollHeight);
window.scrollTo(0, this.$refs.list.scrollHeight);
});
}, 2000);
},
// 获取屏幕高度
beforeMount(height) {
var h =
document.documentElement.clientHeight || document.body.clientHeight;
this.curHeight = h - height; //减去页面上固定高度height
},
},
};
</script>
<style scoped>
.title_warp {
position: fixed;
top: 0px;
left: 0px;
width: 100%;
z-index: 99;
}
.ser_img {
width: 100%;
}
.serve_main {
padding: 180px 20px 120px;
}
.inp_warp {
box-shadow: 0px 2px 12px 0px rgba(0, 0, 0, 0.1);
}
.inp_warp {
padding: 20px 0;
position: fixed;
bottom: 0px;
background-color: #fff;
width: 100%;
left: 0px;
display: flex;
justify-content: flex-start;
align-items: center;
}
.icon_inp {
margin: 0px 20px;
}
.inp {
border: none;
margin-left: 20px;
background-color: #ededed;
padding: 22px 20px;
border-radius: 10px;
width: 66%;
}
</style>
自定义Ttile标签:
这个没有什么技术含量
<template>
<div class="Title">
<!-- 头部组件编写 -->
<div class="top">
<!-- 顶部图标区 -->
<div class="top_icon">
<div class="top_icon_left">
<van-icon
v-if="back"
@click="backHandle"
size="24px"
color="#fff"
name="arrow-left"
/>
</div>
<div class="top_icon_right">
<van-icon
v-if="add"
@click="openAdd"
size="24px"
color="#fff"
name="plus"
class="btn_icon"
/>
<van-icon
v-if="search"
@click="openSearch"
size="24px"
color="#fff"
name="search"
class="btn_icon"
/>
</div>
</div>
<div class="top_title">{{ title }}</div>
</div>
</div>
</template>
<script>
export default {
name: "Title",
props: {
add: {
type: Boolean,
default: false,
},
back: {
type: Boolean,
default: true,
},
title: {
type: String,
default: "登陆",
},
search: {
type: Boolean,
default: false,
},
},
methods: {
// 跳转到搜索页面
openAdd() {
this.$router.push("/article");
},
// 跳转到搜索页面
openSearch() {
this.$router.push("/search");
},
// 返回事件函数
backHandle() {
this.$router.back();
},
},
};
</script>
<style scoped>
.top {
padding: 20px;
background-color: #3c86df;
border-bottom: 1px solid #ececec;
}
.top_title {
margin: 10px;
font-size: 50px;
color: #fff;
}
.top_icon {
display: flex;
justify-content: space-between;
align-items: center;
}
.btn_icon {
margin: 0px 20px;
}
</style>
我发送消息之后的展示(MyChat组件)
<template>
<div class="MyChat">
<div class="my_user">
<div class="time">{{ time }}</div>
<div class="other_main">
<div class="other_message">
{{ con }}
</div>
<div class="triangle_right"></div>
<div class="avatar_warp">
<van-image round width="46" height="46" :src="avatar" />
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: "MyChat",
props: {
time: {
type: String,
default: "",
},
avatar: {
type: String,
default: "",
},
con: {
type: String,
default: "",
},
},
};
</script>
<style scoped>
.time {
text-align: center;
color: #9a9a9a;
padding-bottom: 20px;
}
/* 我的信息 */
.my_user {
padding-bottom: 30px;
}
.other_main {
display: flex;
justify-content: flex-end;
align-items: center;
}
.other_message {
padding: 30px 25px;
border-radius: 20px;
color: #000;
line-height: 34px;
background-color: #ededed;
max-width: 60%;
word-wrap: break-word;
word-break: break-all;
overflow: hidden;
}
.triangle_right {
width: 0;
height: 0;
border-top: 16px solid transparent;
margin-right: 20px;
border-left: 20px solid #ededed;
border-bottom: 16px solid transparent;
}
</style>
对方回消息之后的展示(OtherChat)
<template>
<div class="OtherChat">
<div class="other_user">
<div class="time">{{ time }}</div>
<div class="user_main">
<div class="avatar_warp">
<van-image round width="46" height="46" :src="avatar" />
</div>
<div class="triangle_left"></div>
<div class="user_message">
{{ con }}
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: "OtherChat",
props: {
time: {
type: String,
default: "",
},
avatar: {
type: String,
default: "",
},
con: {
type: String,
default: "",
},
},
};
</script>
<style scoped>
.time {
text-align: center;
color: #9a9a9a;
padding-bottom: 20px;
}
.other_user {
padding-bottom: 30px;
}
.user_main {
display: flex;
justify-content: flex-start;
align-items: center;
}
.user_message {
/* display: inline-block; */
padding: 30px 25px;
border-radius: 20px;
color: #000;
background-color: #ededed;
max-width: 60%;
line-height: 34px;
word-wrap: break-word;
word-break: break-all;
overflow: hidden;
}
.triangle_left {
width: 0;
height: 0;
border-top: 16px solid transparent;
margin-left: 20px;
border-right: 20px solid #ededed;
border-bottom: 16px solid transparent;
}
</style>
这个是兼容,键盘弹起的,移动端好像会出现一开始输入消息,屏幕异常跳动的问题,那个需要设置一下聊天内容页面高度,量一下MyChat和OtherChat组件的高度进行类型操作,就可以处理这个问题,有更好的建议,欢迎打扰,哈哈。