vue 客服腾讯云即时通讯IM 小demo
第一步 按照即时通讯IM文档来安装第二步在main.js 引入第三步 找一个空页面粘贴过去就可以了具体效果图:业务逻辑代码如下:<template><div class="box" v-loading="loading"><div class="t_left"><div class="seek"><el-inputplaceho...
·
第一步 按照即时通讯IM文档来安装
第二步 在main.js 引入
第三步 找一个空页面粘贴过去就可以了
具体效果图:
业务逻辑代码如下:
<template>
<div class="box" v-loading="loading">
<div class="t_left">
<div class="seek">
<el-input
placeholder="请输入用户名进行搜索"
v-model="lookup"
@change = "cLookup"
clearable>
</el-input>
</div>
<div class="t_left_bot" v-if="rListOff">
<div v-for="(item,index) in rList" :key="index"
@click="setZi(item.conversationID,item.userProfile.userID,item.userProfile.avatar,item.userProfile.nick)"
:class="{ active:item.conversationID==isActive }"
>
<img :src="item.userProfile.avatar || defaultAvatar" style="background: #c1c1c1;"/>
<div>{{item.userProfile.nick || item.userProfile.userID}}</div>
<div v-show="item.lastMessage">{{item.lastMessage.messageForShow}}</div>
<div>{{item.lastMessage.lastTime | offTime}}</div>
<div v-if="item.unreadCount != 0"> {{item.unreadCount > 99 ? '99+' : item.unreadCount}}</div>
</div>
</div>
</div>
<div class="t_right" v-if="rRightOff">
<!--名字-->
<div class="t_right_top">{{chatName}}</div>
<!--内容-->
<div class="t_right_con" id="t_right_con">
<div v-if="hList.isCompleted" class="t_r_nmore">没有更多了</div>
<div v-else class="t_r_more" @click="seeMore()">查看更多</div>
<div v-if="hList" >
<div class="hList-left" v-for="(item,index) in hList.messageList" :key="index">
<!--他人发送的消息-->
<div class="hList-left-a" v-if="item.to != toUserId">
<img class="hlAva" :src="avatar || defaultAvatar"/>
<div>
<!-- 判断消息是否是图片 -->
<div v-if="item.type == 'TIMImageElem'" v-html="newImg(item)"></div>
<!-- 判断消息是否是文字或者表情 -->
<div v-if="item.type == 'TIMTextElem'" v-html="emj(item.payload.text)"></div>
<!-- 消息时间 -->
<div>{{item.time | offTime}}</div>
</div>
</div>
<!--我自己发送的消息-->
<div class="hList-left-b" v-else>
<div>
<!-- 判断消息是否是图片 -->
<div v-if="item.type == 'TIMImageElem'" v-html="newImg(item)"></div>
<!-- 判断消息是否是文字或者表情 -->
<div v-if="item.type == 'TIMTextElem'" v-html="emj(item.payload.text)"></div>
<!-- 消息时间 -->
<div>{{item.time | offTime}}</div>
</div>
<img class="hlAva" :src="myAvatar"/>
</div>
</div>
</div>
</div>
<!--输出框-->
<div class="t_right_bot">
<div>
<!-- 发送图片 -->
<div class="icture">
<i class="el-icon-picture"></i>
<input @change="getFile" id="imagePicker" ref="imagePicker" accept="image/gif,image/jpeg,image/jpg,image/png,image/svg" type="file"/>
</div>
<!-- 发送表情 -->
<div class="icture_a">
<el-popover
ref="popover4"
placement="top"
width="400"
height='160'
trigger="click">
<div style="height: 160px; overflow: auto;">
<img style="width:30px; cursor: pointer; margin:1px 2px 3px 4px;" v-for="(item,index) in emojiMap" @click="setEmj(item)" :key="index" :src="emojiUrl+item"/>
</div>
<div slot="reference">
<svg t="1593314000110" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1249" width="28" height="28"><path d="M909.8 305.6c-5.4-10.5-16.3-17.8-28.9-17.8-17.8 0-32.2 14.4-32.2 32.1 0 6 1.7 11.7 4.6 16.5l-0.1 0.1c26.9 52.4 42.1 111.8 42.1 174.7 0 211.6-171.6 383.2-383.2 383.2S128.8 722.9 128.8 511.2 300.4 128.1 512 128.1c62.5 0 121.5 15 173.6 41.5l0.2-0.4c4.6 2.6 10 4.1 15.7 4.1 17.8 0 32.2-14.4 32.2-32.1 0-13.1-7.9-24.4-19.3-29.4C653.6 81 584.9 63.6 512 63.6 264.7 63.6 64.2 264 64.2 511.2S264.7 958.9 512 958.9s447.7-200.4 447.7-447.7c0-74.1-18-144-49.9-205.6z" fill="#4D4D4D" p-id="1250"></path><path d="M771.7 219.7a32.2 32.1 0 1 0 64.4 0 32.2 32.1 0 1 0-64.4 0Z" fill="#4D4D4D" p-id="1251"></path><path d="M368.1 383.8m-45.1 0a45.1 45.1 0 1 0 90.2 0 45.1 45.1 0 1 0-90.2 0Z" fill="#4D4D4D" p-id="1252"></path><path d="M654.2 383.8m-45.1 0a45.1 45.1 0 1 0 90.2 0 45.1 45.1 0 1 0-90.2 0Z" fill="#4D4D4D" p-id="1253"></path><path d="M512.5 739.2c75.7 0 141.2-43.9 172.5-107.6 2.3-4.7 3.8-10 3.8-15.6 0-17.7-14.4-32-32.2-32.1-12.9 0-24.1 7.3-29.2 18.1-20.3 43.5-64.4 73.6-115.4 73.6s-94.4-30.2-114.8-73.5c0-0.1-0.1-0.2-0.1-0.3-5.3-10.6-16.2-17.9-28.9-17.9-17.8 0-32.2 14.4-32.2 32.1 0 5 1.2 9.8 3.3 14.1 30.9 64.5 96.8 109.1 173.2 109.1z" fill="#4D4D4D" p-id="1254"></path></svg>
</div>
</el-popover>
</div>
</div>
<div>
<textarea
placeholder="请输入内容"
v-model="textarea"
maxlength="200"
@keydown="messageSendlisten"
>
</textarea>
<div class="t_limit">当前还可发送{{200-(textarea.length)}}个字</div>
<el-button class="r_i" @click="setButton" type="primary">发送</el-button>
</div>
</div>
</div>
</div>
</template>
<script>
import {emojiMap,emojiUrl} from '../../../assets/emj'
export default {
name: '',
data(){
return{
defaultAvatar:"https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=1651318081,2860235060&fm=26&gp=0.jpg",//默认头像,如果用户没有上传头像或者头像路径错误展示这个路径
myAvatar:"https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=2519824424,1132423651&fm=26&gp=0.jpg",//我的头像
chatName:'',//查看的某个人name
toUserId:'',//查看的某个人id
avatar:'',//查看的某个人avatar
lookup:"",//用户查找的名字
isActive:'1',
loading:false,//加载中
textarea:"",//输入信息
rListOff:false,
rRightOff:false,
emojiMap:emojiMap,
emojiUrl:emojiUrl,
rList:[],//会话列表
hList:[]//具体信息
}
},
filters: {
//接收时间
offTime: function(value) {
var v = value
var date = new Date(v*1000)
var Y = date.getFullYear() + '-'
var M = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1 ) + '-'
var D = (date.getDate() < 10 ? '0' + date.getDate() : date.getDate())
var h = (date.getHours() < 10 ? '0' + date.getHours() : date.getHours()) + ':'
var m = (date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes())
let dt = new Date(Date.parse(new Date()))
let current = dt.getFullYear() +"-"+dt.getMonth()+"-"+dt.getDate()
let system = date.getFullYear() +"-"+date.getMonth()+"-"+date.getDate()
if(current == system){return h + m} else {return Y+M+D}
}
},
methods:{
//消息查看更多
seeMore(){
const self = this
let nextReqMessageID = self.hList.nextReqMessageID
let promise = tim.getMessageList({conversationID:self.hList.messageList[0].conversationID, nextReqMessageID, count: 15});
promise.then(function(imResponse) {
self.hList.messageList = [...imResponse.data.messageList,...self.hList.messageList]
self.hList.nextReqMessageID = imResponse.data.nextReqMessageID; // 分页
self.hList.isCompleted = imResponse.data.isCompleted; // 是否已经拉完
if(self.hList) self.rRightOff = true
});
},
//发送表情
setEmj(val){
let obj = this.emojiMap
let findKey = (value, compare = (a, b) => a === b) =>{
return Object.keys(obj).find(k => compare(obj[k], value))
}
this.$refs.popover4.showPopper = false;
this.textarea = this.textarea.concat(findKey(val))
},
//接收表情判断
emj(value) {
let emImg
for(let key in emojiMap){
emImg = emojiUrl+emojiMap[key]
while(value.indexOf(key) != '-1'){
value = value.replace(key,`<img style='width:25px;' src=${emImg}>`)
}
}
return value
},
//查找用户 模糊查找
cLookup(){
const self = this
let promise = this.tim.getConversationList();
promise.then(function(imResponse) {
self.rList = imResponse.data.conversationList
self.rListOff = true
let arr = []
if(self.lookup.split(" ").join("").length==0){
//业务逻辑
//........
} else {
self.rList.map(item=>{
if(item.userProfile.nick.indexOf(self.lookup)!=-1 || item.userProfile.userID.indexOf(self.lookup)!=-1 ){
arr.push(item)
}
})
}
arr.length == 0 ? self.hlData() : self.rList = arr
}).catch(function(imError) {
console.log(imError)
// console.warn('getConversationList error:', imError); // 获取会话列表失败的相关信息
});
},
//图片上传
getFile(e){
const self = this
// 1. 创建消息实例
self.loading = true
let message = this.tim.createImageMessage({
to: self.toUserId,
conversationType: this.TIM.TYPES.CONV_C2C,
payload: {
file: document.getElementById('imagePicker')
},
onProgress: function(event) { console.log('file uploading:', event) }
});
// 2. 发送消息
let promise = this.tim.sendMessage(message);
promise.then(function(imResponse) {
// 发送成功
self.textarea = ''
self.hList.messageList.push(imResponse.data.message)
self.below()
self.loading = false
}).catch(function(imError) {
console.warn('sendMessage error:', imError);
self.loading = false
});
},
//自动保持在最底部
below(){
this.$nextTick(() => {
let container = this.$el.querySelector("#t_right_con");
container.scrollTop = container.scrollHeight;
})
},
//图片初始化
newImg(val){
let w = val.payload.imageInfoArray[0].width
let h = val.payload.imageInfoArray[0].height
if(w>300){ h = h/(w/300);w = 300}
return `<img style='width:${w}px;height:${h}px;' src='${val.payload.imageInfoArray[0].imageUrl}'/>`
},
//回车发送文本 阻止浏览器默认换行操作
messageSendlisten(event){
if (event.keyCode === 13) {
this.setButton(); // 发送文本
event.preventDefault(); // 阻止浏览器默认换行操作
return false;
}
},
//消息已读
read(id){
let promise = this.tim.setMessageRead({conversationID: id});
promise.then(function(imResponse) {
// 已读上报成功,指定 ID 的会话的 unreadCount 属性值被置为0
}).catch(function(imError) {
// 已读上报失败
console.warn('setMessageRead error:', imError);
});
},
//发送消息
setButton(){
const self = this
if(self.textarea.split(" ").join("").length==0) return this.$message({
message: '请输入正确信息',
type: 'warning'
});
let message = this.tim.createTextMessage({
to: self.hList.userID,
conversationType: this.TIM.TYPES.CONV_C2C,
payload: {
text: self.textarea
}
});
// 发送消息
let promise = tim.sendMessage(message);
promise.then(function(imResponse) {
// 发送成功
self.textarea = ''
self.hList.messageList.push(imResponse.data.message)
self.below()
}).catch(function(imError) {
// 发送失败
console.warn('sendMessage error:', imError);
});
},
//获取会话资料
setZi(conversationID,userID,avatar,chatName){
const self = this
if(userID == self.toUserId) {
return
} else {
self.hList = []
}
self.toUserId = userID
self.avatar = avatar
self.isActive = conversationID
self.chatName = chatName || userID
let promise = this.tim.getMessageList({conversationID: conversationID, count: 15});
promise.then(function(imResponse) {
let hList = {}
hList.messageList = imResponse.data.messageList; // 消息列表。
hList.nextReqMessageID = imResponse.data.nextReqMessageID; // 用于续拉,分页续拉时需传入该字段。
hList.isCompleted = imResponse.data.isCompleted; // 表示是否已经拉完所有消息。
hList.userID = userID; // 点击进去的用户id。
hList.conversationID = conversationID;
self.hList = hList
if(self.hList) self.rRightOff = true
self.below()
//设置消息已读
self.read(conversationID)
});
},
//获取会话列表
hlData(){
const self = this
let promise = this.tim.getConversationList();
promise.then(function(imResponse) {
self.rList = imResponse.data.conversationList
self.rListOff = true
}).catch(function(imError) {
console.log(imError)
});
},
logData(userID,userSig){
const self = this
let promise = this.tim.login({userID: userID, userSig: userSig});
promise.then(function(imResponse) {
//获取会话列表
if (imResponse.data.repeatLogin === true) {
// 标识账号已登录,本次登录操作为重复登录。v2.5.1 起支持
console.log(imResponse.data.errorInfo);
}
}).catch(function(imError) {
console.warn('login error:', imError); // 登录失败的相关信息
});
// 监听事件
this.tim.on(this.TIM.EVENT.SDK_READY, function(event) {
// 收到离线消息和会话列表同步完毕通知,接入侧可以调用 sendMessage 等需要鉴权的接口
self.hlData()
});
this.tim.on(this.TIM.EVENT.MESSAGE_RECEIVED, function(event) {
// 收到推送的单聊、群聊、群提示、群系统通知的新消息,可通过遍历 event.data 获取消息列表数据并渲染到页面
self.hlData()
if(self.hList){
if(event.data[0].from == self.hList.userID){
// console.log('这是正在聊天的聊天界面')
// console.log(event.data[0].conversationID)
self.read(event.data[0].conversationID)
self.hList.messageList.push(event.data[0])
self.below()
}
}
});
},
},
created () {
const self = this
let userID = 'yaohuiqian'
let userSig = 'eJyrVgrxCdYrSy1SslIy0jNQ0gHzM1NS80oy0zLBwpWJ*RmlmYWZiXlQ2eKU7MSCgswUJStDEwMDYwsLM2MLiExqRUFmUSpQ3NTU1MjAwAAiWpKZCxazNAIqtTQzg5qSmQ403M033bzQ2NPFxMvXCWhahXtUWkqwZaJXUFhAhoeBuba2e0Gih3eQibGPo61SLQBhhTHK'
self.logData(userID,userSig)
},
destroyed() {
// 离开页面退出当前账号
let promise = this.tim.logout();
promise.then(function(imResponse) {
console.log(imResponse.data);
}).catch(function(imError) {
console.warn('logout error:', imError);
});
}
}
</script>
<style scoped lang="scss">
@mixin toCla {
color: #a5b5c1;
font-size: 12px;
}
@mixin toClb {
display: inline-block;
>div:nth-child(1):before{
content: "";
width: 0px;
height: 0px;
border-top: 7px solid transparent;
border-bottom: 7px solid transparent;
position: absolute;
top: 10px;
font-size: 7px;
}
>div:nth-child(2){
color: #a5b5c1;
font-size: 12px;
margin-top: 9px;
}
>div:nth-child(1){
outline: none;
font-size: 14px;
position: relative;
max-width: 350px;
word-wrap: break-word;
word-break: break-all;
background: #FFF;
border-radius: 5px;
display: inline-block;
padding: 10px;
-webkit-box-shadow: 0 5px 10px 0 rgba(0,0,0,0.1);
box-shadow: 0 5px 10px 0 rgba(0,0,0,0.1);
margin-top: 20px;
}
}
.box{
width: 90%;
height: 90%;
margin: 0 auto;
margin-top: 2%;
display: flex;
-moz-box-shadow:2px 2px 5px #333333;
-webkit-box-shadow:2px 2px 5px #333333;
box-shadow:2px 2px 5px #333333;
position: relative;
.t_left{
width: 260px;
height: 600px;
overflow: auto;
border-right: 1px solid #e5e5e5;
.seek{
overflow: hidden;
background: red;
position: absolute;
width: 256px;
z-index: 9;
top: 0;
left: 0;
/deep/.el-input__inner{
height: 35px;
border: 0 !important;
border-radius: 0 !important;
border-bottom: 1px solid #b2b2b2 !important;
}
}
.t_left_bot{
padding-top: 36px;
overflow: auto;
>div{
width: 100%;
height: 80px;
background: #FFF;
border-bottom: 1px solid #e5e5e5;
cursor: pointer;
position: relative;
div:nth-child(5){
position: absolute;
width: 18px;
height: 18px;
line-height: 18px;
text-align: center;
font-size: 11px;
top: 4px;
left: 58px;
color: #FFF;
background: red;
border-radius: 50%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
div:nth-child(4){
position: absolute;
width: 80px;
font-size: 14px;
top: 15px;
left: 169px;
color: #b2b2b2;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
text-align: right;
}
div:nth-child(3){
position: absolute;
width: 163px;
font-size: 14px;
top: 45px;
left: 83px;
color: #b2b2b2;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
div:nth-child(2){
position: absolute;
width: 90px;
font-size: 16px;
top: 13px;
left: 83px;
overflow: hidden;
text-overflow:ellipsis;
white-space: nowrap;
}
img{
width: 60px;
height: 60px;
position: absolute;
top: 10px;
left: 10px;
border-radius: 2px;
}
}
}
}
.t_right{
flex: 1;
background: #f3f3f3;
.t_right_bot{
border-top: 1px solid #e5e5e5;
height: 139px;
width: 100%;
background: #FFF;
position: relative;
/deep/.t_limit{
position: absolute;
right: 93px;
bottom: 17px;
color: #b2b2b2;
}
/deep/textarea{
resize: none;
height: 100px;
border: 0;
width: 96%;
display: block;
margin: 0 auto;
outline: none;
}
.r_i{
position: absolute;
right: 12px;
bottom: 7px;
color: #FFF;
}
>div:nth-child(1){
display: flex;
align-items: center;
.icture_a{
display: inline-block;
cursor: pointer;
position: relative;
margin-left: 5px;
/deep/.el-popover{
width: 400px !important;
height: 160px !important;
overflow: auto !important;
}
}
.icture{
width: 30px;
height: 30px;
display: inline-block;
margin: 4px 15px;
cursor: pointer;
overflow: hidden;
position: relative;
}
input[type="file"] {
color: transparent;
}
input{
cursor: pointer !important;
position: absolute;
top: 0;
left: 0;
width: 30px;
height: 30px;
opacity: 0;
}
/deep/i{
font-size: 27px;
}
}
}
.t_right_con{
height: 410px;
overflow: auto;
.t_r_nmore{
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-pack: center;
-ms-flex-pack: center;
justify-content: center;
font-size: 13px;
color: #a5b5c1;
background: 0 0;
padding-left: 0;
padding-right: 0;
cursor: pointer;
}
.t_r_more{
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-pack: center;
-ms-flex-pack: center;
justify-content: center;
font-size: 13px;
color: #409EFF;
background: 0 0;
padding-left: 0;
padding-right: 0;
cursor: pointer;
}
}
.t_right_top{
padding: 0 20px;
color: #1c2438;
font-size: 18px;
font-weight: bold;
line-height: 50px;
text-shadow: #76828c 0 0 0.1em;
}
.hList-left{
width: 100%;
position: relative;
overflow: auto;
.hlAva{
width: 56px;
height: 56px;
border-radius: 50%;
display: block;
display: inline-block;
margin: 10px 20px;
vertical-align: top;
}
.hList-left-b{
text-align: right;
>div:nth-child(2){
@include toCla;
}
>div:nth-child(1){
@include toClb;
>div:nth-child(1):before{
border-top: 7px solid transparent !important;
border-bottom: 7px solid transparent !important;
border-left: 7px solid #FFF !important;
right: -7px !important;
}
}
}
.hList-left-a{
>div:nth-child(3){
@include toCla;
}
>div:nth-child(2){
@include toClb;
>div:nth-child(1):before{
left: -7px !important;
border-right: 7px solid #FFF !important;
}
}
}
}
}
.active{
transition: 300ms;
background: #d6d6d6 !important;
}
}
/* 设置滚动条的样式 */
::-webkit-scrollbar {
width: 3px;
height: 3px;
}
/* 滚动槽 */
::-webkit-scrollbar-track {
border-radius: 10px;
}
/* 滚动条滑块 */
::-webkit-scrollbar-thumb {
border-radius: 10px;
background: rgba(0, 0, 0, 0.1);
}
</style>
更多推荐
已为社区贡献33条内容
所有评论(0)