一、前言
二、主要内容
1、分析实现的逻辑
第一步:用户登录成功之后需要找到目的对象发起私聊。
第二步:数据双向绑定监听输入内容
第三步:点击发送按钮的时候请求后端接口。
第四步:后端建立接口完毕后返回前端聊天数据
第五步:前端触发后端定义的socket.io中接收消息的逻辑
第六步:接收到后台回复的数据,然后进行数据渲染。
2、分步骤实现
2.1用户登录成功之后为每个用户分配一个socket.io
(1)登录成功之后触发后台的
asyn Login(){ ... .... ....省略 //登录成功后跳转到个人中心界面,触发后台定义的方法为每一个登录成功的用户分配一个socketid socket.emit('login', user.username); }
(2)后台新建一个sechma专门用户存放每个用户的socketid
let express = require('express'); let mongoose = require('mongoose'); module.exports = new mongoose.Schema({ socketid:String, username:String });
(3)调用封装的保存用户socketid方法
//=====================封装 let Idtoid = require('../Models/Idtoid') module.exports = class socketHandler{ static async savaUserSocketId(username, socketId){ //保存用户的id和socketid await Idtoid.findOne({ username: username }).then((rs)=>{ if(!rs){ new Idtoid({ username: username, socketid: socketId }).save().then(()=>{ }) }else { Idtoid.update({ username: username }, { socketid: socketId }).then(()=>{ }) } }) } } //=========================调用 socket.on('login', (username)=>{ console.log('有用户登录') socketHandler.savaUserSocketId(username, socketId) })
2.2数据双向绑定触发消息发送的方法
(1)前端data中定义content实现数据双向绑定,(这里需要再调用请求用户信息的方法,请求到当前聊天的用户信息)
data(){ return{ chatId: this.$route.query.chatId, chatName: this.$route.query.chatName, content:'',//发送的对话内容 chatUserInfo:{}, //请求到的聊天的用户信息 chatdataList:[] //当前聊天的信息,自己发的信息存到一个数组中 } },
(2)数据双向绑定完成触发发送
//发送消息 sendContent(){ //1.先判断当前用户是否登录 if(!this.userInfo._id){ Toast("请先登录!") return; } //2.校验输入的内容是否为空 if(this.content==""){ return } //3.请求后端接口 this.$axios.post('/api/chat/chatwith', { chatWithId: this.chatUserInfo._id,//聊天对象 user_id: this.userInfo._id,//当前登录的用户 content:this.content }).then((res)=>{ console.log(res) this.chatdataList.push({//保存发送到的和接收到的消息 user_id:{ avater: this.userInfo.avater }, chatWith:{ _id: this.chatId }, addTime: Date.now(), content: this.content }) //将当前的发送的信息同步更新到vuex中 this.UPDATE_CHATLIST({ _id: this.chatUserInfo._id, from_user: this.chatName, message: this.content, time: Date.now(), me:true,//判别是不是自己发送 avater:this.chatUserInfo.avater }) console.log(this.chatUserInfo._id) console.log(this.chatName) console.log(this.content) console.log(Date.now()) console.log(this.chatUserInfo._id) //发给对方的数据 let data = { from_user: this.userInfo.username, to_user: this.chatName, message: this.content, time: Date.now(), avater: this.userInfo.avater, _id: this.userInfo._id } socket.emit('chat', data) this.content='' }) },
(3)后端实现逻辑
思路:发送消息过来的时候先判断是否两个聊天用户之间已经建立好了联系,如果已经建立好了联系只需要更新聊天数据,如果还没有建立好联系先建立联系。
router.post('/chatwith', (req, res) => { let chatWith = req.body.chatWithId; //获取到前端传来的聊天用户 let user_id = req.body.user_id; //获取到当前登录的用户 let content = req.body.content; //获取到输入的聊天内容 //新建一个聊天内容对象 new ChatContent({ chatWith: chatWith, //聊天用户 user_id: user_id, content: content }).save().then((newContent) => { //查找两个用户之间是否已经建立好了联系 ChatRelation.findOne({ $or:[{ userA:user_id, userB:chatWith },{ userB:user_id, userA:chatWith }] }).then((rs)=>{ //如果已经建立好了联系,只需要更新聊天数组 if (rs){ let chatContent = rs.chatContent; chatContent.unshift(newContent._id); ChatRelation.update({ _id:rs.id },{ chatContent:chatContent }).then(()=>{ res.json({ code:0, data:newContent }) }) }else { //没有建立好联系就新建立联系 new ChatRelation({ userA:user_id, userB:chatWith, chatContent:[newContent._id] }).save().then(()=>{ res.json({ code:0, data:newContent }) }) } }) }) });
这里涉及到的两个sechma对象:
chatContent.js:
let express = require('express') let mongoose = require('mongoose') module.exports = new mongoose.Schema({ user_id:{//当前的用户id type:mongoose.Schema.Types.ObjectId, ref:'User' }, chatWith:{//私信的用户 type:mongoose.Schema.Types.ObjectId, ref:'User' }, addTime:{//发送内容的时间 type:Date, default: Date.now }, content:{ type: String }, unread:{//是否默认未读 type: Boolean, default: true } })
chatRelation.js
let express = require('express'); let mongoose = require('mongoose'); module.exports = new mongoose.Schema({ userA:{ type:mongoose.Schema.Types.ObjectId, ref:'User' }, userB:{ type:mongoose.Schema.Types.ObjectId, ref:'User' }, chatContent:[{ type:mongoose.Schema.Types.ObjectId, ref:'ChatMessage' }], });
(4)消息回复后端逻辑实现
/* 封装socket.io,是为了获取server以便监听 */ let Idtoid = require('../Models/Idtoid') let socketHandler = require('./socketioHander.js'); //socket要实现的具体逻 var socketio = {} var socket_io = require('socket.io') //获取io socketio.getSocketio = function(server){ var io = socket_io.listen(server) io.sockets.on('connection', socket=>{ console.log('连接成功') const socketId = socket.id socket.on('login', (username)=>{ console.log('有用户登录') socketHandler.savaUserSocketId(username, socketId) }) socket.on('chat', (data)=>{ console.log('有用户发起聊天啦') console.log(data) Idtoid.findOne({ username: data.to_user// }).then((rs)=>{ //给对应的人发消息 io.to(rs.socketid).emit('receiveMsg', { from_user: data.from_user, message: data.message, time: data.time, avater:data.avater, _id:data._id }) }) }) }) } module.exports = socketio
(5)前端触发监听后端回复逻辑
//接收对方发来的数据 updateBySocket(){ socket.on('receiveMsg', (data)=>{ //判断是不是当前的对话框 console.log(111111) if(data.from_user == this.chatName){ //把接收到的消息保存到聊天记录中, this.chatdataList.push({ chatWith:{ _id: this.userInfo._id }, user_id:{ avater: data.avater }, addTime: data.addTime, content: data.message }) this.UPDATE_CHATLIST({ _id: this.chatUserInfo._id, from_user: this.chatName, message: data.message, time: data.addTime, me: true, avater: this.chatUserInfo.avater }) } }) },
(6)vuex中的this.UPDATE_CHATLIST方法记录着当前的每一个聊天关系,以及最新消息
//私聊部分,获取到聊天对象的方法 [UPDATE_CHATLIST](state, data){ let flag = 0;//判断新的聊天是否存在于当前的列表中 state.chatList.forEach((item)=>{ if (item.chatWith.username == data.from_user) { flag = 1; if (!data.me) {//判断当前是否在对话框页面中 item.unread++; state.unread++; } //更新 item.content = data.message; item.addTime = data.time; //按添加时间排序 state.chatList.sort((a, b) => { return new Date(b.addTime) - new Date(a.addTime) }); //跳出循环 return false; } }); //是新的并且不在对话框页面 if (!flag&&!data.me) { //添加到第一条 state.chatList.unshift({ chatWith: { avater: data.avater, username: data.from_user, _id: data._id }, addTime: data.time, content: data.message, unread: 1 }); state.unread++; }else if (!flag&&data.me){//新的并且在对话框页面,不需要增加unread state.chatList.unshift({ chatWith: { avater: data.avater, username: data.from_user, _id: data._id }, addTime: data.time, content: data.message, }); } },