页面效果如下图
1、先获取消息记录接口,展示到页面中
data(){
return{
value:'',//发送的内容
list:[
// 数据结构如下图,只需要内容 和状态 判断是 客服消息 还是用户消息
// {
// text:'请问有什么可以帮您?',
// status:1,//1客服,0用户
// },
],//消息列表
upDataMsg:null,// 查询新消息,轮询
}
},
mounted() {
this._initData();// 获取聊天列表
//轮询,每隔10秒查询一次是否有新消息
this.upDataMsg = setInterval(() => {
this.axios.post('User/csMsgCheck',{
cs_id:this.$route.query.uid,//客服id
}).then(res=>{
if(res.code == 1){
if(res.data.has_new == 1){
console.log('接口返回1 说明有新数据,重新请求接口即可')
this._initData();
}
}
})
}, 10000);
},
methods:{
// 写一个公共方法,获取消息记录
_initData(){
this.axios.post('User/csMsgList',{
id:this.$route.query.uid,// 客服id
}).then(res=>{
if(res.code == 1){
if(res.data.data.length>0){ // 有数据得情况下
this.list = res.data.data.map(item=>{
// 在这里组合成我需要的数据结构
return {
msg_type:item.msg_type,//消息类型,判断是文字,还是图片
text:item.content,//消息内容
status:item.from_who == 'user' ? 0 : 1,//0用户信息,1客服信息
}
});
this.$nextTick(() => {
//此处操作,是让聊天得内容区域 滚动条自动滚动到底部
var container = document.querySelector('.content')
container.scrollTop = container.scrollHeight
})
}else{
let obj = {
msg_type:'text',
text:'请问有什么可以帮您?',
status:1,//1客服,0用户
}
this.list.push(obj);
}
}
})
},
//上传图片
upFile(e){
let file = e.target.files[0]
// 图片开始压缩,获得压缩后的rst参数
lrz(file, { quality: 0.3 }).then(rst => {
let userfile = this.upfile(rst.base64, rst.origin.name);//调用自己的upfile方法吧压缩后的base64转二进制
let formdata = new FormData();
formdata.append('file', userfile);
this.axios.post('/Common/upload', formdata).then(res => {
if (res.code == 1) {
this.value = res.data.url;
this.up('image');//图片上传成功后执行一次发送方法
}
})
e.target.value = '';//重置一遍value,至关重要,防止第二次上传相同图片,或者取消上传报错
});
},
//发送内容
up(type){
if(this.value == ''){
this.$Message.error('请输入您想要发送的内容')
}else{
this.axios.post('User/csMsgUpload',{
cs_id:this.$route.query.uid,// 客服id
msg_type: type == 'image' ? 'image':'text', // 判断是发送的图片还是文字
content:this.value,
}).then(res=>{
if(res.code == 1){
let obj = {}
if(type == 'image'){
obj = {
msg_type:type,//图片类型
text:this.value,//图片
status:0,//1客服,0用户
}
}else{
obj = {
msg_type:type,//文字类型
text:this.value,//文本内容
status:0,//1客服,0用户
}
}
this.list.push(obj);
this.value = '';
this.$nextTick(() => {
//滚动条位置到底
var container = document.querySelector('.content')
container.scrollTop = container.scrollHeight
})
}
})
}
},
},
destroyed(){
//离开页面,结束轮询
clearInterval(this.upDataMsg)
},
2、页面v-for循环list 数组
<!-- 聊天内容区域 -->
<div class="content">
<div class="item" v-for="(item,index) in list" :key="index">
<!-- 客服 status == 1 ,这里得客服回复得消息是富文本,所以要用 v-html -->
<div class="kf" v-if="item.status == 1">
<img src="@img/icon/12.png" alt="客服默认头像" class="logo">
<div class="text" v-html="item.text"></div>
<img :src="link+item.text" alt="" v-if="item.msg_type == 'image'">
</div>
<!-- 用户 , 用户发的消息分两种,图片和文字, 判断msg_type得类型即可-->
<div class="user" v-else>
<p v-if="item.msg_type == 'text'">{{item.text}}</p>
<img :src="item.text" alt="" v-if="item.msg_type == 'image'">
<img src="@img/icon/13.png" alt="客服默认头像" class="logo">
</div>
</div>
</div>
页面完整代码如下
<template>
<div class="kefu">
<headerTop :bgColor="true"></headerTop>
<div class="container">
<h2>客服列表 > 联系客服</h2>
<div class="page">
<div class="title">{{nickname}}</div>
<!-- 聊天内容区域 -->
<div class="content">
<div class="item" v-for="(item,index) in list" :key="index">
<!-- 客服回复 -->
<div class="kf" v-if="item.status == 1">
<img src="@img/icon/12.png" alt="客服" class="logo">
<div class="text" v-html="item.text"></div>
<!-- <p v-if="item.msg_type == 'text'">{{item.text}}</p> -->
<img :src="link+item.text" alt="" v-if="item.msg_type == 'image'">
</div>
<!-- 用户回复 -->
<div class="user" v-else>
<p v-if="item.msg_type == 'text'">{{item.text}}</p>
<img :src="link+item.text" alt="" v-if="item.msg_type == 'image'">
<img src="@img/icon/13.png" alt="用户" class="logo">
</div>
</div>
</div>
<div class="bom">
<div class="img">
<img class="img1" src="@img/icon/14.png" alt="">
<img class="img2" src="@img/icon/15.png" alt="高亮状态" style="display: none;">
<input type="file" accept="image/png,image/jpeg" @change="upFile($event)">
</div>
<div class="val">
<textarea placeholder="输入你想要问的问题" v-model="value"></textarea>
</div>
<div class="btn">
<button @click="up('text')">发送</button>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import headerTop from "@/components/header";
export default {
components: {
headerTop,
},
data(){
return{
link:localStorage.getItem('hostLink'),//图片路径
nickname:this.$route.query.nickname,//客服名称
value:'',//发送的内容
list:[
// {
// text:'请问有什么可以帮您?',
// status:1,//1客服,0用户
// },
],//消息列表
// 查询新消息,轮询
upDataMsg:null,
}
},
mounted() {
// 获取聊天历史记录
this._initData();
//轮询接收新消息
this.upDataMsg = setInterval(() => {
this.axios.post('User/csMsgCheck',{
cs_id:this.$route.query.uid,
}).then(res=>{
if(res.code == 1){
if(res.data.has_new == 1){
console.log('有新消息')
this._initData();
}
}
})
}, 10000);
},
methods:{
_initData(){
this.axios.post('User/csMsgList',{
cs_id:this.$route.query.uid,
}).then(res=>{
if(res.code == 1){
if(res.data.data.length>0){
this.list = res.data.data.map(item=>{
return {
msg_type:item.msg_type,
text:item.content,
status:item.from_who == 'user' ? 0 : 1,//0用户信息,1客服信息
}
});
this.$nextTick(() => {
var container = document.querySelector('.content')
container.scrollTop = container.scrollHeight
})
}else{
let obj = {
msg_type:'text',
text:'请问有什么可以帮您?',
status:1,//1客服,0用户
}
this.list.push(obj);
}
}
})
},
//上传图片
upFile(e){
let file = e.target.files[0]
// 图片开始压缩,获得压缩后的rst参数
lrz(file, { quality: 0.3 }).then(rst => {
let userfile = this.upfile(rst.base64, rst.origin.name);//调用自己的upfile方法吧压缩后的base64转二进制
let formdata = new FormData();
formdata.append('file', userfile);
this.axios.post('/Common/upload', formdata).then(res => {
if (res.code == 1) {
this.value = res.data.url;
this.up('image');
}
})
e.target.value = '';//重置一遍value,至关重要,防止第二次上传相同图片,或者取消上传报错
});
},
//发送内容
up(type){
if(this.value == ''){
this.$Message.error('请输入您想要发送的内容')
}else{
this.axios.post('User/csMsgUpload',{
cs_id:this.$route.query.uid,
msg_type: type == 'image' ? 'image':'text',
content:this.value,
}).then(res=>{
if(res.code == 1){
let obj = {}
if(type == 'image'){
obj = {
msg_type:type,
text:this.value,//图片
status:0,//1客服,0用户
}
}else{
obj = {
msg_type:type,
text:this.value,//文本内容
status:0,//1客服,0用户
}
}
this.list.push(obj);
this.value = '';
this.$nextTick(() => {
var container = document.querySelector('.content')
container.scrollTop = container.scrollHeight
})
}
})
}
},
},
destroyed(){
//离开页面,结束轮询
clearInterval(this.upDataMsg)
},
}
</script>
<style lang="less" scoped>
.kefu{
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
background: #fff;
h2{
height: 87px;
line-height: 87px;
font-size:22px;
color:rgba(51,51,51,1);
letter-spacing:1px;
border-bottom: 1px solid #eee;
margin-bottom: 32px;
}
.page{
margin-bottom: 100px;
.title{
padding-left: 30px;
width:100%;
height:80px;
line-height: 80px;
background:rgba(37,91,253,1);
font-size:18px;
font-weight:bold;
color:rgba(255,255,255,1);
letter-spacing:1px;
}
.content{
width: 100%;
height: 446px;
overflow-y: scroll;
border-top: none;
border: 1px solid #ccc;
padding: 30px 30px;
background: #F9F9F9;
/deep/.item{
margin-bottom: 30px;
.kf{
display: flex;
justify-content: flex-start;
.logo{margin-right: 20px;}
}
.user{
display: flex;
justify-content: flex-end;
.logo{margin-left: 20px;}
}
.logo{
width: 61px;
height: 60px;
}
img{
max-width: 600px;
height: auto;
}
p{
display: flex;
align-items: center;
padding: 10px 20px;
max-width: 600px;
font-size:18px;
font-weight:bold;
color:rgba(51,51,51,1);
line-height:25px;
letter-spacing:1px;
background: #fff;
}
}
}
.bom{
padding: 20px 30px;
height: 260px;
background: #fff;
position: relative;
border: 1px solid #979797;
border-top: none;
.img{
position: relative;
width: 30px;
height: 30px;
img{
width: 30px;
height: 30px;
position: absolute;
left: 0;
top: 0;
}
input{
position: absolute;
left: 0;
top: 0;
opacity: 0;
width: 30px;
height: 30px;
}
}
.img:hover{
.img1{
display: none;
}
.img2{
display: block!important;
}
}
.val{
margin-top: 20px;
height: 120px;
padding-bottom: 20px;
textarea{
width: 100%;
height: 100%;
font-size: 18px;
line-height: 1.7;
resize: none;
border: none;
}
}
.btn{
display: flex;
justify-content: flex-end;
button{
cursor: pointer;
width:140px;
height:54px;
background:rgba(37,91,253,1);
border-radius:2px;
font-size:24px;
font-weight:bold;
color:rgba(255,255,255,1);
letter-spacing:1px;
border: none;
}
}
}
}
}
</style>