一、首先配置全局websocket
创建webSocket.js:
// global.js
export default {
ws: {},
setWs: function(newWs) {
this.ws = newWs
}
}
main.js引入:
import webSocket from './utils/webSocket.js'
Vue.prototype.$webSocket = webSocket
在其他页面使用:
this.$webSocket.ws
二、App.vue监听websocket
created() {
this.initWebsocket();
}
methods: {
initWebsocket() {
let that = this
if ('WebSocket' in window) {
that.ws = new WebSocket('ws://127.0.0.1:30000/ws')
that.$webSocket.setWs(that.ws)
that.ws.onopen = function () {
setTimeout(() => {
that.queryMessage()
}, 500)
}
//that.onopen(); 这个地方未定义是会报错,所以我写成了 that.ws.onopen = function() {console.log('开始连接')};
that.ws.onclose = function () {
// 关闭 websocket
console.log('连接已关闭...')
setTimeout(() => {
that.initWebsocket()
}, 2000)
}
} else {
// 浏览器不支持 WebSocket
console.log('您的浏览器不支持 WebSocket!')
}
},
queryMessage() {
let that = this;
let customerList = this.$store.state.permission.customerList;
let expertList = this.$store.state.permission.expertList;
for (let index = 0; index < customerList.length; index++) {
let msg = {
type: "login",
uid: customerList[index].id
};
if (that.$webSocket.ws && that.$webSocket.ws.readyState == 1) {
that.$webSocket.ws.send(JSON.stringify(msg));
}
}
for (let index = 0; index < expertList.length; index++) {
let msg = {
type: "login",
uid: expertList[index].id
};
if (that.$webSocket.ws && that.$webSocket.ws.readyState == 1) {
that.$webSocket.ws.send(JSON.stringify(msg));
}
}
that.$webSocket.ws.onmessage = function (res) {
let jsonData = JSON.parse(res.data);
let data = jsonData.data;
//新推过来条数
let customerList = that.$store.state.permission.customerList;
for (let index = 0; index < customerList.length; index++) {
if(jsonData.uid == customerList[index].id){
axios.post("获取总条数接口",{uid:customerList[index].id}).then(res=>{
if(res.data.success == true){
if(customerList[index].msgNum == null){
customerList[index].msgNum=0;
customerList[index].msgNum+=res.data.data;
}
}
})
if (data.type == "newConv") {
if (customerList[index].id != data.send_uid) {
customerList[index].msgNum++;
break;
}
} else if (data.type == "newMsg") {
if (customerList[index].id != data.send_uid) {
customerList[index].msgNum++;
break;
}
}
}
}
that.$store.state.permission.customerList = customerList;
//新推过来条数
let expertList = that.$store.state.permission.expertList;
for (let index = 0; index < expertList.length; index++) {
if(jsonData.uid == expertList[index].id){
axios.post("获取总条数接口",{uid:expertList[index].id}).then(res=>{
if(res.data.success == true){
if(expertList[index].msgNum == null){
expertList[index].msgNum=0;
expertList[index].msgNum+=res.data.data;
}
}
})
if (data.type == "newConv") {
if (expertList[index].id != data.send_uid) {
expertList[index].msgNum++;
break;
}
} else if (data.type == "newMsg") {
if (expertList[index].id != data.send_uid) {
expertList[index].msgNum++;
break;
}
}
}
}
that.$store.state.permission.expertList = expertList;
};
}
}
}
三、聊天界面
<template>
<div class="ContactWrap" v-if="uid != 0">
<div class="Contact">
<div class="ContactSide">
<!-- <div class="ContactSide-tip"></div> -->
<div >
<div v-for="(item,index) in userList" :key="index" >
<div class="ContactItem" @click="chooseUser(item)" :class="{ChooseItem : item.uid == chooseId}">
<img class="UserAvator" :src="item.avatar" />
<div class="UserContent">
<div class="UserMsg">
<span class="UserName" style="padding-left:5px;">{{item.nickname}}</span>
<span class="MsgTime">{{item.last_msg_time.split(' ')[0]}}</span>
</div>
<div class="UserSnippet" style="padding-top:8px;" :title="title">{{customMethod(item.msg_type,item.msg_content)}}</div>
<span v-if="item.unreadCount != 0" style="float:right; display:inline-block; border-radius:5px 5px 5px 5px;line-height: 90px;height:15px;margin-left:100px;margin-top:-15px;color:rgb(255 255 255);font-size:10px;background-color:#cccccc;display:flex;justify-content:center;align-items:center;">{{item.unreadCount}}</span>
</div>
</div>
</div>
</div>
</div>
<div class="ContactBox">
<div class="ContactBox-header" style="font-size:18px;">{{titename}}</div>
<div class="MessageBox" ref="MessageBox">
<div
v-for="(item,index) in chatList"
:key="index"
class="Message"
:style="item.send_uid == uid?'flex-direction:row-reverse':''"
>
<!-- <div class="UserHead"> -->
<img :src="item.send_uid == uid? pcimg:duifangimg" class="UserAvator" />
<!-- </div> -->
<div class="UserMsg" :class="item.send_uid == uid?'RightMessage':'LeftMessage'">
<span v-if="item.msg_type == 1" :style="item.send_uid == uid?' float: right;':''">{{item.msg_content}}</span>
<span
:style="item.send_uid == uid?' float: right;':''"
v-if=" item.msg_type == 2 "
>
<video
style="width: 160px"
controls="controls"
:src="item.msg_content"
alt=""
srcset=""
></video>
</span>
<span
:style="item.send_uid == uid?' float: right;':''"
v-if="item.msg_type == 4 "
>
<img
v-image-preview
:src="item.msg_content"
alt=""
srcset=""
style="
vertical-align: text-top;
display: inline-block;
width: 40px;
height:40px
"
/>
</span>
<span
:style="item.send_uid == uid?' float: right;':''"
v-if="item.msg_type == 3 "
>
<audio
style="width: 200px"
controls="controls"
:src="item.msg_content"
alt=""
srcset=""
></audio>
</span>
</div>
</div>
</div>
<!-- 输入框 -->
<div class="InputBox">
<div v-if="titename != ''">
<textarea v-model="msg" style="resize:none" class="InputTextarea" rows="3" ></textarea>
<div class="InputBox-footer">
<!-- <div class="FooterDesc">按Enter键发送</div> -->
<button class="sendButton" @click="sendmsg($event)">发送</button>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import util from "@/utils/index.js";
import axios from "axios";
export default {
name: "Chat",
components: {},
data() {
return {
uid: 0,
ws: null,
msg: "", //聊天信息
chatList: [
// {
// id: 14,
// content: "哈哈哈",
// avaUrl:
// "https://pic4.zhimg.com/v2-a12b2d609fa2d5d16c10ea069419f3c3_xs.jpg"
// },
// {
// id: 1,
// content: "嘿嘿嘿",
// avaUrl:
// "https://pic4.zhimg.com/v2-a12b2d609fa2d5d16c10ea069419f3c3_xs.jpg"
// },
], //聊天记录
userList: [
// {
// uid:1,
// username: "test1",
// content: "美美哒",
// time: "6-22",
// avaUrl:
// "https://pic4.zhimg.com/v2-a12b2d609fa2d5d16c10ea069419f3c3_xs.jpg"
// },
// {
// uid:2,
// username: "test2",
// content: "活好每一天",
// time: "6-22",
// avaUrl:
// "https://pic4.zhimg.com/v2-a12b2d609fa2d5d16c10ea069419f3c3_xs.jpg"
// },
],
chooseId:0,
titename:"",
title: "",
conv_id:"",
duifangimg:"",
};
},
props:['fuid','pcimg'],
mounted() {
this.uid = this.fuid;//可以自己写个假数据
setTimeout(() => {
this.getChatList();
}, 300);
//this.initWebsocket();
},
watch:{ //监听value的变化,进行相应的操做便可
fuid: function(a,b){ //a是value的新值,b是旧值
this.uid = this.fuid;
this.chatList =[]
this.getChatList();
}
},
methods: {
//发送信息
sendmsg(event) {
event.preventDefault()
let msg = {"type":"send","send_id":this.uid,"receive_id":this.chooseId,"msg_type":"1","content":this.msg};
if (this.$webSocket.ws && this.$webSocket.ws.readyState == 1) {
this.$webSocket.ws.send(JSON.stringify(msg));
}
setTimeout(() => {
this.getMsgList(this.conv_id);
}, 500)
this.msg = "";
},
//滚动条滚动到底部
scrollBottm() {
let el = this.$refs["MessageBox"];
el.scrollTop = el.scrollHeight;
},
//选择联系人
chooseUser(user){
this.chooseId = user.uid;
this.titename = user.nickname;
this.getMsgList(user.conv_id);
},
//获取聊天列表
getChatList(){
axios.post("聊天列表接口",{uid:this.uid}).then(res=>{
if(res.data.success == true){
this.userList = res.data.data;
if(this.userList.length>0){
this.chooseId = this.userList[0].uid;
this.titename = this.userList[0].nickname;
this.conv_id = this.userList[0].conv_id;
this.getMsgList(this.userList[0].conv_id);
this.duifangimg = this.userList[0].avatar;
}else{
this.chooseId = '';
this.titename = '';
}
}
})
},
//获取聊天记录
getMsgList(chooseId){
axios.post("聊天记录接口",{cid:chooseId,uid:this.uid,pagesize:'10'}).then(res=>{
if(res.data.success == true){
this.chatList = res.data.data;
let msgNums = 0;
for(let i=0;i<this.userList.length;i++){
if(this.userList[i].conv_id == chooseId){
msgNums = this.userList[i].unreadCount;
this.userList[i].unreadCount = 0;
this.$emit('getInitData',this.fuid,msgNums);
}
}
setTimeout(() => {
this.scrollBottm();
}, 50);
}
})
},
//隐藏字符串
customMethod(type,msg){
if(type == '1'){
if(msg.length<10){
return msg;
}else{
this.title=msg.substring(0,10)+'...';
return msg.substring(0,10)+'...';
}
}else {
this.title='媒体消息';
return '[媒体消息]';
}
},
//重新计算未读消息数
getUnreadNum(msgNums){
//把未读消息数清空
// for (let index = 0; index < this.$store.state.permission.customerList.length; index++) {
// if(this.fuid == this.$store.state.permission.customerList[index].id){
// if(this.$store.state.permission.customerList[index].msgNum != null && this.$store.state.permission.customerList[index].msgNum >= msgNums){
// this.$store.state.permission.customerList[index].msgNum = this.$store.state.permission.customerList[index].msgNum - msgNums;
// }else{
// this.$store.state.permission.customerList[index].msgNum = 0;
// }
// }
// }
// for (let index = 0; index < this.$store.state.permission.expertList.length; index++) {
// if(this.fuid == this.$store.state.permission.expertList[index].id){
// if(this.$store.state.permission.expertList[index].msgNum != null && this.$store.state.permission.expertList[index].msgNum >= msgNums){
// this.$store.state.permission.expertList[index].msgNum = this.$store.state.permission.expertList[index].msgNum - msgNums;
// }else{
// this.$store.state.permission.expertList[index].msgNum = 0;
// }
// }
// }
}
}
}
</script>
<style lang="scss" scoped>
.Contact {
height: 704px;
width: 100%;
background-color: #fff;
border: 1px solid #ebebeb;
box-shadow: 0 0 4px 0 rgba(26, 26, 26, 0.1);
border-radius: 3px;
display: flex;
//联系人
.ContactSide {
width: 500px;
height: 100%;
border-right: 1px solid #ebebeb;
.ContactSide-tip {
height: 60px;
line-height: 40px;
font-weight: 600;
padding: 0 30px;
border-bottom: 1px solid #ebebeb;
}
}
.ContactItem {
padding: 12px 20px 12px 29px;
cursor: pointer;
display: flex;
border-bottom: 1px solid #f7f8fa;
.UserContent {
flex: 1;
.UserMsg {
display: flex;
align-items: center;
justify-content: space-between;
.UserName {
font-size: 15px;
columns: #444444;
font-weight: 600;
}
.MsgTime {
font-size: 12px;
color: #999999;
float: right;
}
}
}
}
.ChooseItem{
background: #f5f4f4;
}
.UserAvator {
width: 40px;
height: 40px;
border-radius: 50%;
margin-right: 10px;
}
.ContactBox {
width: 100%;
&-header {
background: #fafafa;
font-size: 15px;
// margin: 0 14px;
// height: 20px;
padding-bottom: 21px;
padding-top: 22px;
border-bottom: 1px solid #ebebeb;
font-weight: 600;
text-align: center;
}
//聊天框
.MessageBox {
height: 470px;
overflow: scroll;
.Message {
display: flex;
margin: 20px;
.UserMsg {
max-width: 388px;
border-radius: 8px;
padding: 6px 12px;
font-size: 14px;
position: relative;
margin: 0 8px;
text-align: left;
white-space: pre-wrap;
word-break: break-all;
}
.LeftMessage {
background-color: #f6f6f6;
color: #444;
&::after {
content: "";
position: absolute;
width: 8px;
height: 8px;
left: -4px;
top: 14px;
background: #f6f6f6;
-webkit-transform: rotate(45deg);
transform: rotate(45deg);
}
}
.RightMessage {
background-color: #0084ff;
color: #fff;
&::after {
content: "";
position: absolute;
width: 8px;
height: 8px;
right: -4px;
top: 14px;
background: #0084ff;
-webkit-transform: rotate(45deg);
transform: rotate(45deg);
}
}
}
}
//输入框
.InputBox {
padding: 0 10px;
border-top: 1px solid #ebebeb;
display: flex;
flex-direction: column;
justify-content: space-between;
height: 168px;
background: #fff;
z-index: 10;
.InputTextarea {
margin-top: 20px;
width: 100%;
border: none;
font-size: 14px;
flex: 1;
height:90px;
}
&-footer {
display: flex;
align-items: center;
justify-content: flex-end;
height: 40px;
.FooterDesc {
font-size: 14px;
color: #bfbfbf;
padding-right: 10px;
}
.sendButton {
color: #fff;
background-color: #0084ff;
border-radius: 6px;
width: 72px;
height: 32px;
font-size: 13px;
line-height: 32px;
text-align: center;
}
}
}
}
}
</style>
版权声明:本文为qq_41992943原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。