![](https://i-blog.csdnimg.cn/blog_migrate/141ecc031dc738a3e2a969be4c994109.png)
利用WebSocket长连接实现即使通讯,可以实现个人单独聊天,内容不需要存服务器,这里只有前端代码,后端负责传递消息和用户列表。
<template>
<div>
<div class="content" style="display: flex;">
<div class="leftChat">
<div v-for="(item,index) in peopleList" :key="index" @click="userchange(item,index)"
:class="indexChekck == index ? 'peopelCheck' : 'people'">
<img :src=" item.avatar ? baseUrl + item.avatar : require('@/assets/image/profile.png')" alt=""
style="width: 50px;height: 50px;"> <span style="width: 60px;font-size: 16px;">{{item.userName}}</span>
<div class="xaioxi" v-if="item.number >0">{{ item.number >=99 ? '99+' : item.number+ '+' }}</div>
</div>
<div v-if="peopleList.length ==0">暂无人在线</div>
</div>
<div v-if="showPeo" style="position: relative;">
<div class="top">
<img :src=" topeople.avatar ? baseUrl + topeople.avatar : require('@/assets/image/profile.png')"
alt="" style="width: 50px;height: 50px;border-radius: 50%;margin-left: 5px;">
<span style="margin-left: 10px;">{{topeople.userName}}</span>
</div>
<!-- 聊天内容 -->
<div id="message" style="" ref="message">
<div v-for="(item,idnexs) in this.allPeople[this.chatIndex].chatList" :key="idnexs"
style="position: relative;">
<div class="left" v-if="item.class =='left'">
<div style="text-align: left;font-size: 14px;">{{item.name }}</div>
<div style="margin: 10px 0;text-align: left;">
<!-- <div class="leftText">{{ item.message }}</div>
<img :src="baseUrl + item.img" alt=""> -->
<div v-html="item.message" class="leftText"></div>
</div>
</div>
<div class="right" v-if="item.class =='right'">
<div style="text-align: right;font-size: 14px;">{{item.name }}</div>
<div style="margin: 10px 0;text-align: right;">
<!-- <div class="rightText">{{ item.message }}</div> -->
<div v-html="item.message" class="rightText"></div>
</div>
</div>
</div>
</div>
<div id="text" class="editable-container" :contenteditable="true" @keydown="textareaKeydown($event)"></div>
<!-- <div id="text">
</div>
<el-input text="text" id="text" @keydown="textareaKeydown($event)" :v-model="messages"></el-input> -->
<div @click="sendMessage" class="sendBtn">发送消息</div>
</div>
</div>
</div>
</template>
这是简单界面,实现简单聊天,只能和在线的人聊天,无法和不在线的聊天,
getIs() {
let that = this
//判断当前浏览器是否支持WebSocket
if ('WebSocket' in window) {
websocket = new WebSocket("ws://自己的地址" );
} else {
alert('Not support websocket');
}
//接收到消息的回调方法
websocket.onmessage = function (event) {
that.showMessage(event.data);
let data = JSON.stringify(event.data);
if (data.dataType == 3) {
alert(data.message);
}
}
},
showMessage(message) {
let data = JSON.parse(message);
let messages = ''
if (data.dataType == 1) {
let arr = []
if (this.allPeople.length != 0) {
arr = this.peopleList
}
this.peopleList = data.userList
this.peopleList.forEach(item => {
for (let i = 0; i < arr.length; i++) {
if (item.userId == arr[i].userId) {
item.number = arr[i].number ? arr[i].number : 0
}
}
})
let array = data.userList
if (this.allPeople.length != 0) {
this.allPeople.forEach((item) => {
for (let i = 0; i < array.length; i++) {
let n = 0
let m = 0
if (item.id != array[i].userId) {
n++
} else {
m++
}
if (n >= array.length) {
this.allPeople.push({
id: array[i].userId,
chatList: [],
number: 0,
isTrue: true
})
}
if (m >= 1) {
item.isTrue = true
} else {
item.isTrue = false
}
}
})
} else {
for (let i = 0; i < array.length; i++) {
this.allPeople.push({
id: array[i].userId,
chatList: [],
number: 0,
isTrue: true
})
}
}
// console.log('所有列表', this.allPeople);
} else if (data.dataType == 2) {
messages = data.message
this.allPeople.forEach((item, index) => {
if (item.id == data.fromUserId) {
this.allPeople[index].chatList.push({
class: 'left',
message: data.message,
name: data.fromUserName,
// img: '/profile/signature/2021/12/09/64c9f089-b0fb-4a3f-a51b-8240369a09d6.jpeg',
})
}
})
this.peopleList.forEach(item => {
if (item.userId == data.fromUserId && data.fromUserId != this.toUserId) {
item.number++
}
})
this.$forceUpdate()
// console.log('人员信息', this.peopleList);
this.$emit('newchatChange')
// console.log('dsadsad', this.allPeople[this.chatIndex]);
this.$nextTick(() => {
let ele = document.getElementById("message");
//判断元素是否出现了滚动条
if (ele) {
ele.scrollTop = ele.scrollHeight;
}
//设置滚动条到最底部
})
} else if (data.dataType == 4) {
this.$message('对方已掉线,您发送的消息对方已经看不到了')
}
// console.log('data', data, this.peopleList);
// document.getElementById('messages').innerHTML += messages + '<br/>';
},
userchange(val, index) {
this.indexChekck = index
this.showPeo = true
this.topeople = val
this.toUserId = val.userId
// console.log('点击了', val);
this.allPeople.forEach((item, index) => {
if (item.id == this.toUserId) {
this.chatIndex = index
this.allPeople[this.chatIndex].number = 0
}
})
this.peopleList.forEach(item => {
if (item.userId == this.toUserId) {
item.number = 0
}
})
this.$nextTick(() => {
let ele = document.getElementById("message");
//判断元素是否出现了滚动条
if (ele) {
ele.scrollTop = ele.scrollHeight;
}
//设置滚动条到最底部
})
// console.log('dsadsadasdsad', this.allPeople[this.chatIndex]);
},
//监听按键操作
textareaKeydown(event) {
if (event.ctrlKey && event.keyCode === 13) {
event.preventDefault() // 阻止浏览器默认换行操作
} else if (event.keyCode === 13) {
//enter
this.sendMessage();
event.preventDefault() // 阻止浏览器默认换行操作
return false
}
},
//发送消息
sendMessage() {
// if (websocket) {
// // websocket.close();
// } else {
// this.getIs()
// }
var msgBak = document.getElementById('text').innerHTML;
if (msgBak != null && msgBak != "") {
// console.log('消息列表', this.chatList);
let data = {
toUserId: this.toUserId,
message: msgBak
}
var map2json = JSON.stringify(data)
if (map2json.length < 8000) {
websocket.send(map2json);
document.getElementById('text').innerHTML = null;
this.allPeople[this.chatIndex].chatList.push({
class: 'right',
message: msgBak,
name: localStorage.getItem('nickName')
})
// console.log('发送消息', this.allPeople);
this.$nextTick(() => {
let ele = document.getElementById("message");
//判断元素是否出现了滚动条
//设置滚动条到最底部
ele.scrollTop = ele.scrollHeight;
})
} else {
this.$message('文本或者图片太长了,少写一点吧')
document.getElementById('text').innerHTML = null;
}
} else {
this.$message('请输入内容')
}
this.messages = null
},
js部分实现用户列表查询,发送消息,以及接收消息,有消息会提示,
一下是全部代码
<template>
<div>
<!-- <div id="messages"></div> -->
<div class="content" style="display: flex;">
<div class="leftChat">
<div v-for="(item,index) in peopleList" :key="index" @click="userchange(item,index)"
:class="indexChekck == index ? 'peopelCheck' : 'people'">
<img :src=" item.avatar ? baseUrl + item.avatar : require('@/assets/image/profile.png')" alt=""
style="width: 50px;height: 50px;"> <span style="width: 60px;font-size: 16px;">{{item.userName}}</span>
<div class="xaioxi" v-if="item.number >0">{{ item.number >=99 ? '99+' : item.number+ '+' }}</div>
</div>
<div v-if="peopleList.length ==0">暂无人在线</div>
</div>
<div v-if="showPeo" style="position: relative;">
<div class="top">
<img :src=" topeople.avatar ? baseUrl + topeople.avatar : require('@/assets/image/profile.png')"
alt="" style="width: 50px;height: 50px;border-radius: 50%;margin-left: 5px;">
<span style="margin-left: 10px;">{{topeople.userName}}</span>
</div>
<!-- 聊天内容 -->
<div id="message" style="" ref="message">
<div v-for="(item,idnexs) in this.allPeople[this.chatIndex].chatList" :key="idnexs"
style="position: relative;">
<div class="left" v-if="item.class =='left'">
<div style="text-align: left;font-size: 14px;">{{item.name }}</div>
<div style="margin: 10px 0;text-align: left;">
<!-- <div class="leftText">{{ item.message }}</div>
<img :src="baseUrl + item.img" alt=""> -->
<div v-html="item.message" class="leftText"></div>
</div>
</div>
<div class="right" v-if="item.class =='right'">
<div style="text-align: right;font-size: 14px;">{{item.name }}</div>
<div style="margin: 10px 0;text-align: right;">
<!-- <div class="rightText">{{ item.message }}</div> -->
<div v-html="item.message" class="rightText"></div>
</div>
</div>
</div>
</div>
<div id="text" class="editable-container" :contenteditable="true" @keydown="textareaKeydown($event)"></div>
<!-- <div id="text">
</div>
<el-input text="text" id="text" @keydown="textareaKeydown($event)" :v-model="messages"></el-input> -->
<div @click="sendMessage" class="sendBtn">发送消息</div>
</div>
</div>
</div>
</template>
<!-- <script src="./"></script> -->
<script>
let websocket = null;
export default {
data() {
return {
messagesss: null,
chatIndex: -1,
allPeople: [], //所有人的消息
indexChekck: -1,
chatList: [],
showPeo: false,
topeople: {},
toUserId: null,
baseUrl: process.env.VUE_APP_BASE_API,
peopleList: [],
messages: null,
id: localStorage.getItem('userId'),
shakeList: [],
}
},
mounted() {
let time = null
time = setInterval(() => {
if (websocket) {
websocket.close();
this.getIs()
} else {
this.getIs()
}
}, 30000);
this.getIs()
},
methods: {
getIs() {
let that = this
//判断当前浏览器是否支持WebSocket
if ('WebSocket' in window) {
websocket = new WebSocket("ws://自己的地址" + this.id);
} else {
alert('Not support websocket');
}
//接收到消息的回调方法
websocket.onmessage = function (event) {
that.showMessage(event.data);
let data = JSON.stringify(event.data);
if (data.dataType == 3) {
alert(data.message);
}
}
},
showMessage(message) {
let data = JSON.parse(message);
let messages = ''
if (data.dataType == 1) {
let arr = []
if (this.allPeople.length != 0) {
arr = this.peopleList
}
this.peopleList = data.userList
this.peopleList.forEach(item => {
for (let i = 0; i < arr.length; i++) {
if (item.userId == arr[i].userId) {
item.number = arr[i].number ? arr[i].number : 0
}
}
})
let array = data.userList
if (this.allPeople.length != 0) {
this.allPeople.forEach((item) => {
for (let i = 0; i < array.length; i++) {
let n = 0
let m = 0
if (item.id != array[i].userId) {
n++
} else {
m++
}
if (n >= array.length) {
this.allPeople.push({
id: array[i].userId,
chatList: [],
number: 0,
isTrue: true
})
}
if (m >= 1) {
item.isTrue = true
} else {
item.isTrue = false
}
}
})
} else {
for (let i = 0; i < array.length; i++) {
this.allPeople.push({
id: array[i].userId,
chatList: [],
number: 0,
isTrue: true
})
}
}
// console.log('所有列表', this.allPeople);
} else if (data.dataType == 2) {
messages = data.message
this.allPeople.forEach((item, index) => {
if (item.id == data.fromUserId) {
this.allPeople[index].chatList.push({
class: 'left',
message: data.message,
name: data.fromUserName,
// img: '/profile/signature/2021/12/09/64c9f089-b0fb-4a3f-a51b-8240369a09d6.jpeg',
})
}
})
this.peopleList.forEach(item => {
if (item.userId == data.fromUserId && data.fromUserId != this.toUserId) {
item.number++
}
})
this.$forceUpdate()
// console.log('人员信息', this.peopleList);
this.$emit('newchatChange')
// console.log('dsadsad', this.allPeople[this.chatIndex]);
this.$nextTick(() => {
let ele = document.getElementById("message");
//判断元素是否出现了滚动条
if (ele) {
ele.scrollTop = ele.scrollHeight;
}
//设置滚动条到最底部
})
} else if (data.dataType == 4) {
this.$message('对方已掉线,您发送的消息对方已经看不到了')
}
// console.log('data', data, this.peopleList);
// document.getElementById('messages').innerHTML += messages + '<br/>';
},
userchange(val, index) {
this.indexChekck = index
this.showPeo = true
this.topeople = val
this.toUserId = val.userId
// console.log('点击了', val);
this.allPeople.forEach((item, index) => {
if (item.id == this.toUserId) {
this.chatIndex = index
this.allPeople[this.chatIndex].number = 0
}
})
this.peopleList.forEach(item => {
if (item.userId == this.toUserId) {
item.number = 0
}
})
this.$nextTick(() => {
let ele = document.getElementById("message");
//判断元素是否出现了滚动条
if (ele) {
ele.scrollTop = ele.scrollHeight;
}
//设置滚动条到最底部
})
// console.log('dsadsadasdsad', this.allPeople[this.chatIndex]);
},
//监听按键操作
textareaKeydown(event) {
if (event.ctrlKey && event.keyCode === 13) {
event.preventDefault() // 阻止浏览器默认换行操作
} else if (event.keyCode === 13) {
//enter
this.sendMessage();
event.preventDefault() // 阻止浏览器默认换行操作
return false
}
},
//发送消息
sendMessage() {
// if (websocket) {
// // websocket.close();
// } else {
// this.getIs()
// }
var msgBak = document.getElementById('text').innerHTML;
if (msgBak != null && msgBak != "") {
// console.log('消息列表', this.chatList);
let data = {
toUserId: this.toUserId,
message: msgBak
}
var map2json = JSON.stringify(data)
if (map2json.length < 8000) {
websocket.send(map2json);
document.getElementById('text').innerHTML = null;
this.allPeople[this.chatIndex].chatList.push({
class: 'right',
message: msgBak,
name: localStorage.getItem('nickName')
})
// console.log('发送消息', this.allPeople);
this.$nextTick(() => {
let ele = document.getElementById("message");
//判断元素是否出现了滚动条
//设置滚动条到最底部
ele.scrollTop = ele.scrollHeight;
})
} else {
this.$message('文本或者图片太长了,少写一点吧')
document.getElementById('text').innerHTML = null;
}
} else {
this.$message('请输入内容')
}
this.messages = null
},
}
}
</script>
<style lang="scss">
.editable-container {
img {
max-width: 190px !important;
max-height: auto !important;
}
}
.leftText img {
max-width: 190px !important;
max-height: auto !important;
}
.rightText img {
max-width: 190px !important;
max-height: auto !important;
}
</style>
<style lang="scss" scoped>
.top {
display: flex;
align-items: center;
border-bottom: rgb(167, 162, 162) 1px solid;
height: 60px;
line-height: 60px;
width: 500px;
background: #f5f5f5;
}
.content {
position: fixed;
// position: absolute;
top: 100px;
right: 60px;
z-index: 999;
}
#message {
padding: 10px;
overflow: hidden;
overflow: scroll;
width: 500px;
background: #f5f5f5;
height: 400px;
}
.sendBtn {
font-size: 14px;
color: #07c160;
border-radius: 5px;
position: absolute;
right: 20px;
bottom: 10px;
height: 30px;
line-height: 30px;
padding: 0 10px 0 10px;
text-align: center;
background: white;
}
.sendBtn:hover {
background: #cecbcb;
}
#text {
padding: 10px;
border-top: 1px rgb(167, 162, 162) solid;
background: #f5f5f5;
width: 500px;
height: 140px;
overflow: hidden;
overflow: scroll;
img {
width: 70px !important;
height: 70px !important;
}
}
#text::-webkit-scrollbar {
display: none
}
.leftChat {
min-width: 100px;
padding: 10px;
background: #e6e6e6;
border-right: 1px rgb(167, 162, 162) solid;
overflow: hidden;
overflow: scroll;
height: 600px;
.xaioxi {
position: absolute;
top: 0px;
right: 10px;
width: 25px;
height: 15px;
border-radius: 10px;
background: red;
color: white;
font-size: 12px;
line-height: 15px;
text-align: center;
}
// /deep/ .el-badge__content,
// .el-badge__content--undefined,
// .is-fixed {
// top: 5px;
// right: 35px;
// }
.people {
position: relative;
border-bottom: 1px solid rgb(167, 162, 162);
margin-top: 10px;
padding-bottom: 5px;
display: flex;
align-items: center;
span {
margin-left: 10px;
}
}
.peopelCheck {
position: relative;
border-bottom: 1px solid rgb(167, 162, 162);
margin-top: 10px;
padding-bottom: 5px;
display: flex;
align-items: center;
background: #cac8c6;
span {
margin-left: 10px;
}
}
}
// 去除滚动条显示
#message::-webkit-scrollbar {
display: none
}
// 去除滚动条显示
.leftChat::-webkit-scrollbar {
display: none
}
.left {
// width: 100%;
// float: left;
margin-top: 20px;
margin-right: 0px;
}
.right {
// width: 100%;
margin-top: 20px;
// float: right;
margin-right: 0px;
}
.rightText {
margin-right: 0;
line-height: 1.5;
padding: 10px 20px;
border-radius: 15px;
color: #f7f8f8;
background-color: #00a5ff;
width: auto;
max-width: 80%;
display: inline-block;
text-align: start;
// width: 100%;
line-height: 25px;
white-space: normal;
word-break: break-all;
word-wrap: break-word;
// min-width: 200px;
// min-height: 200px;
img {
width: 150px !important;
height: 150px !important;
}
}
.leftText {
white-space: normal;
word-break: break-all;
word-wrap: break-word;
margin-left: 0px;
line-height: 1.5;
padding: 10px 20px;
border-radius: 15px;
color: black;
background-color: #ffffff;
width: auto;
max-width: 80%;
display: inline-block;
}
</style>