vue+websocket+nodejs实现聊天室 -一对多、一对一聊天
前言:上篇我们讲了如何使用vue+websocket加nodejs搭建一个聊天室,下面我们在该聊天室基础上增加了一对一单聊功能。支持一对一单聊,一对多群聊。...
·
前言
上篇我们讲了如何使用vue + websocket + nodejs搭建一个聊天室,
下面我们在该聊天室基础上增加了一对一单聊功能。支持一对一单聊,一对多群聊。
先看效果:
大概思路:
主要通过参数brige区分是群聊还是单聊:
brige为空是群聊;
brige包含了当前登录人的uid还有聊天对应人的uid,是单聊。
核心代码:
// 单聊
if (obj.brige && obj.brige.length) {
obj.brige.forEach(item => {
conns[item].sendText(JSON.stringify(obj))
})
return;
}
// 群聊(目前是默认写死的一个群)
server.connections.forEach(function (conn) { // 注意:这里是server,不是ws
conn.sendText(JSON.stringify(obj)) // 注意:这里得转成字符串发送过去,不然会报错。
})
流程图如下:
上代码:
服务端:
const ws = require('nodejs-websocket')
const moment = require('moment')
let users = []
let conns = {}
function broadcast(obj) {
// 单聊
if (obj.brige && obj.brige.length) {
obj.brige.forEach(item => {
conns[item].sendText(JSON.stringify(obj))
})
return;
}
server.connections.forEach(function (conn) {
conn.sendText(JSON.stringify(obj))
})
}
const server = ws.createServer(function (conn) {
conn.on('text', function (data) {
const obj = JSON.parse(data)
switch (obj.type) {
case 1:
{
// 将所有uid对应的连接都保存到一个对象里
conns[obj.uid] = conn;
// 不存在uid对应的用户(不是本人),才会添加,避免重复
const isSelf = users.some(m => m.uid === obj.uid)
console.log(isSelf, data.uid, users, '所有用户')
if (!isSelf) {
users.push({
nickname: obj.nickname,
uid: obj.uid
})
}
broadcast({
type: 1,
nickname: obj.nickname,
uid: obj.uid,
msg: `${obj.nickname}进入了聊天室`,
date: moment().format('YYYY-MM-DD HH:mm:ss'),
users,
brige: obj.brige
})
}
break;
case 2:
// 聊天时候不需要users,type为1已经处理了
broadcast({
type: 2,
nickname: obj.nickname,
uid: obj.uid,
msg: obj.msg,
users,
date: moment().format('YYYY-MM-DD HH:mm:ss'),
brige: obj.brige
})
break;
}
})
conn.on('close', function (e) {
console.log(e, '服务端连接关闭')
})
conn.on('error', function (e) {
console.log(e, '服务端异常')
})
}).listen(8888)
console.log('服务端已开启')
客户端:
视图层:(由之前的class为right改为现在web-im。多加了左侧菜单栏,其他地方跟上篇一致)
<div class="web-im">
<div class="left">
<div class="user" @click="triggerGroup()">
群一
</div>
<div class="user" v-for="(itm, idex) in users" :key="idex" v-show="itm.uid !== uid" @click="triggerUser(itm)">
<span>{{itm.nickname}}</span>
</div>
</div>
<div class="right">
<div class="body im-record" id="im-record">
<p>{{title}}</p>
<div class="ul">
<!-- user为靠右展示样式,如果uid一致说明是本人 -->
<div class="li" :class="{user: item.uid == uid}" v-for="(item, index) in currentMessage" :key="index">
<template v-if="item.type===1">
<p class="join-tips">{{item.msg}}</p>
</template>
<template v-else>
<p class="message-date">
<span class="m-nickname">{{item.nickname}}</span> {{item.date}}</p>
<p class="message-box">{{item.msg}}</p>
</template>
</div>
</div>
</div>
<div class="im-footer">
<el-input placeholder="请输入你想说的内容..." v-model="msg" class="im-footer_inp"/>
<el-button class="im-footer_btn" type="primary" @click="send">发送</el-button>
</div>
</div>
</div>
逻辑层:(之前是聊天框数组是messageList,现在改成currentMessage)
(注:…表示代码跟之前一致,这里不再多写)
computed: {
// 筛选当前brige一致的放到一个聊天数组里,区分单聊和群聊
currentMessage () {
const vm = this
let data = this.messageList.filter(item => {
return item.brige.sort().join('') === vm.brige.sort().join('')
})
return data
}
},
...
// 发送信息给客户端
sendMessage (type, msg) {
const data = {
uid: this.uid,
type,
nickname: this.nickname,
msg,
users: this.users,
brige: this.brige
}
this.ws.send(JSON.stringify(data))
this.msg = ''
},
// 切换到单聊
triggerUser (itm) {
this.brige = [this.uid, itm.uid]
this.title = `和${itm.nickname}聊天`
},
// 切换到群聊
triggerGroup () {
this.brige = []
this.title = '群聊'
},
样式层:
.web-im {
display: flex;
}
.left {
width: 200px;
border: 1px solid #ccc;
.user {
width: 100%;
height: 36px;
padding-left: 10px;
border-bottom: 1px solid #ccc;
line-height: 36px;
.msgtip {
display: inline-block;
width: 20px;
height: 20px;
background: #46b0ff;
margin-left: 5px;
text-align: center;
color: #fff;
line-height: 20px;
border-radius: 50%;
}
}
}
.right {
position: relative;
flex: 1;
height: 600px;
margin: 0 auto;
.im-title {
height: 30px;
padding-left: 20px;
border-bottom: 1px solid #ccc;
line-height: 30px;
font-size: 16px;
}
.im-footer {
position: absolute;
bottom: 0;
left: 0;
display: flex;
width: 100%;
.im-footer_inp {
width: 80%;
}
.im-footer_btn {
width: 20%;
}
}
.im-record {
width: 100%;
height: 540px;
overflow-y: auto;
.join-tips {
position: relative!important;
display: block;
width: 100%;
left: 0!important;
transform: none!important;
color: #cccccc;
font-size: 15px;
text-align: center;
}
.li {
position: relative;
margin-bottom: 15px;
text-align: left;
color: #46b0ff;
&:after {
content: '';
display: block;
clear: both;
}
.message-date {
font-size: 16px;
color: #b9b8b8;
}
.m-nickname {
color: #46b0ff;
}
&.user {
text-align: right;
}
}
.message-box {
line-height: 30px;
font-size: 20px;
}
}
}
更多推荐
已为社区贡献1条内容
所有评论(0)