前言
前几篇我们讲了如何创建聊天室,一对一/一对多聊天,以及加已读未读消息状态。
这篇主要讲如何创建群聊和加入群聊。
还是跟之前一样,本文只写新加入的逻辑,之前逻辑请查看之前文章。
PS:效果稍微有点粗糙哈哈,不要在意这些细节~
先看效果:
大概思路:
通过传不同类型的type,区分是创建群还是添加群。
1、如果是10,创建群聊,我们就将群名称,以及生成的群id,存入groups里面,并且把创建群聊的人默认加入到群
2、如果是20,加入群聊,我们根据要加入的群id,找到对应的群,并把需要加入的人,加入到群
3、发送消息,判断是否有群id,如果没有表示一对一,逻辑不变。如果有群id,则去groups里面找到对应的群,并拿出群下面所有的user,根据id,找到对应的conn(用户连接),发送消息。
流程图:
创建群:
加入群:
上代码:
服务端:
(…表示跟之前逻辑一致,不在多写)
...
收到客户端数据处理:
(主要是增加了type为10和20, 1和2只是新增了groups字段,gorups需全局定义)
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,groups})}break;case 2: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,status: 1, // 表示未读groups,groupId: obj.groupId})break;case 10:groups.push({id: moment().valueOf(),name: obj.name,users: [{nickname: obj.nickname,uid: obj.uid}]})broadcast({type: 1,nickname: obj.nickname,uid: obj.uid,date: moment().format('YYYY-MM-DD HH:mm:ss'),msg: `${obj.nickname}创建了${obj.name}群`,brige: obj.brige,users,groups})break;case 20:// 找到当前群,往当前群的userspush进去登录人信息currentGroup = groups.filter(item => { return item.id === obj.groupId })[0]currentGroup.users.push({uid: obj.uid,nickname: obj.nickname})broadcast({type: 1,nickname: obj.nickname,uid: obj.uid,date: moment().format('YYYY-MM-DD HH:mm:ss'),msg: `${obj.nickname}加入了${obj.groupName}群`,brige: obj.brige,users,groups,groupId: obj.groupId // 有是群聊,没有是单聊})break;}function broadcast(obj) {... (跟之前逻辑一致,以下为新增:)// 如果是有groupId代表是群消息if (obj.groupId) {// 找到对应群currentGroup = groups.filter(item=>{return item.id === obj.groupId})[0];// 变量群里面的任意,发送消息currentGroup.users.forEach(item=>{conns[item.uid].sendText(JSON.stringify(obj));})return;}
}
客户端
视图层:
// 这个dialog可自己写,此处只做参考。(新建群模态框)<el-dialog:visible="showGroupDialog"><el-input type="text" v-model="groupName" placeholder="请输入群名称" ></el-input><span slot="footer" class="dialog-footer"><el-button @click="showGroupDialog = false">取 消</el-button><el-button type="primary" @click="createGroup">确 定</el-button></span></el-dialog>// 这块主要是群展示做了更改,之前是默认写死了个群,现在是数组遍历得到。
<div class="left"><p class="user" @click="showGroupDialog = true">新建群</p><div class="user" v-for="items in groups" :key="items.id" @click="triggerGroup(items)"><span> {{items.name}}</span><span v-if="!isUserInGroup(items)" @click="addGroup(items.id)">+</span><span class="msgtip" v-show="getGroupMsgNum(items)">{{getGroupMsgNum(items)}}</span></div><div class="user" v-for="(itm, idex) in users" :key="idex" v-show="itm.uid !== uid" @click="triggerUser(itm)"><span>{{itm.nickname}}</span><span class="msgtip" v-show="getMsgNum(itm)">{{getMsgNum(itm)}}</span></div></div>
逻辑层:
export default {data () {return {...groups: [], // 所有群对应数组showGroupDialog: false, // 新建群模态框groupName: '', // 群名groupId: ''}}computed: {// 筛选当前brige一致的放到一个聊天数组里,区分单聊和群聊currentMessage () {const vm = thislet data = this.messageList.filter(item => {// 如果有groupId,过滤展示出当前对应群if (this.groupId) {return item.groups.filter(p => { return p.id === this.groupId })} else {return item.brige.sort().join('') === vm.brige.sort().join('')}})data.forEach(m => {m.status = 0})return data}},methods: {// 发送信息给客户端sendMessage (type, msg) {const data = {...groups: this.groups,groupId: this.groupId}this.ws.send(JSON.stringify(data))this.msg = ''},// 创建群createGroup () {const data = {uid: this.uid,type: 10,nickname: this.nickname,users: this.users,name: this.groupName,brige: []}this.ws.send(JSON.stringify(data))this.showGroupDialog = falsethis.groupName = ''},// 加入群addGroup (id) {const data = {uid: this.uid,type: 20,nickname: this.nickname,brige: [],groupName: this.groupName,groupId: id}this.ws.send(JSON.stringify(data))},// 判断当前用户是否在群里isUserInGroup (items) {const isIn = items.users.some(item => { return item.uid === this.uid })return isIn},// 获取单聊消息未读数量getMsgNum (user) {// userid相同,确认是当前聊天对应人的消息数组return this.messageList.filter (m => {return m.brige.length && m.status === 1 && m.uid === user.uid}).length},// 获取群聊未读消息数getGroupMsgNum (users) {return this.messageList.filter (m => {return m.groupId === users.id && m.status === 1}).length},triggerUser (itm) {this.brige = [this.uid, itm.uid]this.title = `和${itm.nickname}聊天`},triggerGroup (items) {const isIn = items.users.some(item => { return item.uid === this.uid})if (!isIn) {this.$message.error('您还不是该群成员,不可发信息!');return;}this.groupId = items.idthis.brige = []this.title = `在${items.name}聊天`},// 连接websocketcontactSocket () {...ws.onmessage = function (e) {const obj = JSON.parse(e.data)that.messageList.push(obj)if (obj.users) that.users = obj.usersif (obj.groups) that.groups = obj.groups}}
}
样式层:
使用elementui框架组件,无自定义样式。
参考链接:Node + WebSocket + Vue 聊天室创建群聊/加入群聊功能 – 第五章