基于Vue全家桶的仿QQ即时聊天应用
尝试使用Vue做了一个较为完整的全栈项目,支持私聊、登录注册和根据QQ号搜索聊天。觉得不错就请点个star吧!
在线项目地址:http://weqq.link (chrome手机端浏览)
- 聊天功能
- 登录功能
- 注册功能
- 搜索新建聊天
- 个人主页
- 联系人页面
- 空间动态页
- Vue
- Vue-router
- Vuex
- axios
- Sass
- Swiper
- Express
- Webpack
- JsonWebToken
- Mongodb
- Socket.io
运行前需要在相应文件下修改监听地址
dev-server.js下的mongodb地址,默认监听27017
mongoose.connect('mongodb://localhost:27017/test',{useMongoClient:true})
app.vue下的websocket地址,默认监听9090
this.$store.commit('socket',io.connect('http://localhost:9090/'))
当中使用到了sendcloud邮件功能,在routes.js下修改为你注册的信息
var apiuser = YOUR_APIUSER
var apikey = YOUR_APIKEY
安装依赖包
npm install //建议使用国内源 cnpm install
运行开发环境
npm run dev //运行
前端,使用vuex做状态管理
//连接服务端socket
this.$store.commit('socket',io.connect('http://localhost:8080/'))
//向服务端发送信息
this.getSocket.emit('message','Hello!') //mapGetters获取socket
//接收来自服务端的信息
this.getSocket.on('message',(msg)=>{
console.log(msg)
})
//加入指定房间
this.getSocket.emit('join',roomid)
后端
//socket服务端的监听
var server = app.listen(port)
var io = require('socket.io')(server)
io.on('connection',(socket)=>{
//接收和发送信息
socket.on('message',(msg)=>{
console.log(msg) //接收的信息
//向指定房间发送信息
io.sockets.in(roomid).emit('message','服务端收到你的消息了哦')
})
//加入指定房间
socket.on('join',(roomid)=>{
socket.join(roomid)
})
//断开连接
socket.on('disconnect',()=>{
console.log('断开连接')
})
})
从列表页进入某聊天房。存储在store中后再在聊天页读取
toChat(chatroom){
//inroom存储当前进入的聊天房的信息
var inroom = {
roomid:chatroom.roomid,
roomname:chatroom.roomname,
type:chatroom.type,
receiver:chatroom.receiver,
chatMsgList:chatroom.chatMsgList,
lastReadAt:chatroom.lastReadAt
}
this.$store.commit('changeInroom',inroom)
}
接收新消息后,更新本地聊天信息、显示时间、房间顺序
this.getSocket.on('message',(msg)=>{
var chatMsg = {
isMe:msg.qqnumber==this.getUser.qqnumber,
roomid:msg.roomid,
username:msg.username,
avatar:msg.avatar,
msg:msg.msg,
time:this.$moment.tz(msg.time,'Asia/Shanghai').format(),
type:msg.type
}
for(let i = 0;i<this.getChatRoom.length;i++){
if(this.getChatRoom[i].roomid==chatMsg.roomid){
//更新本地聊天信息、显示时间、房间顺序
this.$store.state.chatroom[i].chatMsgList.push(chatMsg)
this.$store.state.chatroom[i].lastChatAt = msg.time
var lastChatroom = this.$store.state.chatroom[i]
this.$store.state.chatroom.splice(i,1)
this.$store.state.chatroom.splice(0,0,lastChatroom)
}
}
})
计算未读信息的数量。因为mongodb存储的是零时区,所以用moment将时间本地化了
//本地化时间
localTime(time){
return this.$moment.tz(time,'Asia/Shanghai')
},
//未读信息数量
unread(chatroom){
var unreadMsg = []
if(chatroom.chatMsgList.length>0){
for(let i = 0;i<chatroom.chatMsgList.length;i++){
//moment比较时间,对比此房间最后一条消息的更新时间 和 我阅读此房间消息的时间
if(this.$moment(this.localTime(chatroom.chatMsgList[i].time)).isAfter(this.localTime(chatroom.lastReadAt))){
unreadMsg.push(chatroom.chatMsgList[i])
}
}
return unreadMsg.length
}else{
return chatroom.unread = 0
}
}
使用连接socket时设置的socket.id来使陌生人之间通信,接收到消息后给自己本地添加对应的聊天房
//来自对方添加房间的请求
this.getSocket.on('addroom',(chatroom)=>{
//添加到聊天列表
this.$store.commit('addChatRoom',{
roomid:chatroom.roomid,
roomname:chatroom.roomname,
email:chatroom.email,
receiver:chatroom.receiver,
imgsrc:chatroom.imgsrc,
lastChatAt:new Date(),
lastReadAt:new Date()-1000*10,
type:chatroom.type,
chatMsgList:[]
})
//添加房间socket监听
this.getSocket.emit('join',{
username:this.getUser.username,
chatroom:[chatroom]
})
})
用debounce自动延迟调用搜索方法
searching: _.debounce(function(){
this.$store.commit('changeSearchNotice','搜索中...')
this.isCalculating = true
if(this.$validate.isNotEmpty(this.searchtext)&&this.$validate.qqnumber(this.searchtext)){
this.$store.dispatch('searchUser',{
condition:this.searchtext
})
setTimeout(function(){
this.isCalculating = false
}.bind(this),1000)
}else{
this.$store.commit('changeSearchNotice','未找到您所查询的用户')
}
},500)
app.get('/chatmsg',function(req,res){
var email = req.query.email
ChatMsg.find({'$or':[{email:email},{receiver:email}]}).sort({'time':1}).exec((err,chatmsg)=>{
if(err){
console.log(err)
}else{
res.json({
success:true,
chatmsg:chatmsg
})
}
})
})
使用的sendcloud邮件服务,对QQ的支持不错
app.post('/send_email',function(req,res){
receiveEmail = req.body.email
sendEmailTime = new Date().getTime()/1000
//检查发送间隔
req.session.sendCount += 1
if(req.session.sendCount>1&&sendEmailTime-req.session.sendTime<30){
res.json({
success:false,
notice:'发送过于频繁。'
})
}else{
req.session.sendTime = sendEmailTime
console.log('session:'+req.session.sendTime)
//生成验证码
var char = ['1','2','3','4','5','6','7','8','9','0','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z']
var random;
verificationCode = ''
for(let i=0;i<6;i++){
do{
random = Math.floor(Math.random()*100)
}while(random>=char.length)
verificationCode+=char[random];
}
//使用sendcloud发送邮件
var apiuser = YOUR_APIUSER
var apikey = YOUR_APIKEY
var from = 'mail@weqq.link'
var from_name = 'WeQQ'
var subject = '欢迎使用WeQQ'
var html = '<p>您本次操作的验证码为:'+verificationCode+'</p><p>10分钟内有效。</p>'
var sc = new Sendcloud(apiuser,apikey,from,from_name)
var response = res
sc.send(receiveEmail,subject,html).then(function(info){
response.json({
success: true,
result: info.message
})
})
}
})
其实就是想实践一下JWT的方法,用传统的cookie验证一样没什么毛病的。
app.get('/verifyToken',function(req,res){
var email = req.query.email
var token = req.query.token
if(token&&email){
User.findOne({email:email}).exec((err,user)=>{
if(user){
jwt.verify(token,user.secretKey,(err,decoded)=>{
if(err){
res.json({
success:false,
destroy:true,
notice:'登录状态已失效。'
})
}else{
console.log('token验证成功')
console.log(decoded)
res.json({
success:true,
notice:'token验证成功',
email:user.email
})
}
})
}else{
res.json({
success:false,
notice:'您还未注册!'
})
}
})
}else{
res.json({
success:false,
notice:'未提供token!'
})
}
})
const mongoose = require('mongoose')
const Schema = mongoose.Schema
const crypto = require('crypto');
var ChatRoomSchema = new Schema({
roomid:String,
email:String,
receiver:String,
type:String,
roomname:String,
imgsrc:String,
joinAt:{
type:Date,
default:Date.now()
},
lastChatAt:Date,
lastReadAt:Date
},{collection:'ChatRoom'})
ChatRoomSchema.pre('save',function(next){
//保存私聊房间ID
if(this.type=='single'){
var arr = []
arr.push(this.md5Crypt(this.email),this.md5Crypt(this.receiver))
this.roomid = arr.sort().join('')
}else{
this.roomid = this.md5Crypt(this.email)
}
next()
})
ChatRoomSchema.methods = {
md5Crypt:function(obj){
var md5 = crypto.createHash('md5')
var result = md5.update(obj).digest('hex')
return result
}
}
var ChatRoom = mongoose.model('ChatRoom',ChatRoomSchema)
module.exports = ChatRoom
登录页面
注册页面
聊天页面
搜索页面