• vue2.0开发聊天程序(八) 初步完成


    图片描述图片描述图片描述图片描述图片描述

    项目地址

    服务器源码地址:https://github.com/ermu592275254/chat-socket
    网页源码地址:https://github.com/ermu592275254/chat-socket

    项目设计概述

    相关技术

    nodejs

    使用nodejs搭建后台,因为是一个单页应用,并且前后端通信使用了webSocket,所有只用http模块搭建一个简单的服务器,未使用koa、express等web框架。

    webSocket

    使用socket.io实现webSocket,前端通过import socket.io 的方式会出现不断重连的情况,于是使用script方式实现。

    const io = require('socket.io-client');
    // or with import syntax
    import io from 'socket.io-client';
    
    // or script
    <script src="/socket.io/socket.io.js"></script>
    <script>
      const socket = io('http://localhost');
    </script>
    

    mongodb

    使用mongoose操作mongodb。mongodb这类非关系型数据库,功能较关系型数据库阉割了许多。主要表现在复杂的sql语句、事务支持等。

    vue

    使用vue以及vue的衍生产品,同时用到bootstarp作为样式框架。简单兼容了PC和移动。(PC仅支持chrome,在firefox、ie等浏览器中,会出现样式、布局混乱的情况)。

    功能点实现

    私聊

    通过用户名和socketId进行匹配。保存用户每次登录的socketId,当对方在线时,将此信息通过socketId发送给对方。不在线仅保存到数据库,用户上线即可在私聊中查看。目前不支持消息通知,也不支持未读消息

    ...// 每次登录都将socketId替换为当前登录的socketId
      userModel.update({username: data.username}, {socketId: socket.id}).then(res => {
                        socket.emit('login', user);
                    }).catch(err => {
                        console.log(err);
                        socket.emit('err', 'update user socketId was failed');
                    });
    ...
      chatModel.findOne({sendTime: time}).populate('sender receiver').then(newChat=>{
                            let receiverData = {
                                receiver: data.sender,
                                data: newChat
                            };
                            // 如果对方在线就发送给对方
                            if (io.sockets.connected[user.socketId]) {
                                io.sockets.connected[user.socketId].emit('newMessage', receiverData);
                            }
                            let senderData = {
                                receiver: data.receiver,
                                data: newChat
                            };
                            // 同时也发送给自己(也可直接在前端添加,后端不发送)
                            io.sockets.connected[socket.id].emit('newMessage', senderData);
                        }).catch(err=>{
                            io.sockets.connected[socket.id].emit('err', 'can`t find the newMessage')
                        })

    群聊

    通过broadcast实现组发送。将群、群对应的聊天记录保存在数据库。用户进入群聊,则将其加入到对应的broadcast中。

     socket.on('joinRoom', function(data) {
                if (!common.checkData(data)) {
                    io.sockets.connected[socket.id].emit('err', 'request params Can`t be empty');
                    return;
                }
                // 加入对应的群聊
                socket.join(data.groupName, function() {
                    let roomName = Object.keys(socket.rooms);
                    io.to(data.groupName, `${data.username} has joined the room`);
                    socket.broadcast.in('data.groupName').emit('newUserJoin', {
                        groupName: data.groupName,
                        username: data.username
                    })
                });
            })
     groupChatModel.findOne({'sendTime': time}).populate('sender').then(res=>{
                            if(res){
                                // 发送给自己
                                io.sockets.connected[socket.id].emit('newMsgOfGroup', res);
                                // 将消息发送给群里的所有人除了自己
                                socket.broadcast.in(data.groupName).emit('newMsgOfGroup', res);
                            } else {
                                io.sockets.connected[socket.id].emit('err', 'the message data is null');
                            }

    头像上传

    同样使用webSocket,将头像ID保存在用户信息表中,将图片文件保存在服务器static文件夹中。

     uploadIcon(){
        let file = this.$refs.uploadEl.files[0];
        console.log(file);
        if(file.size > 100000){
            this.Toast('文件大小不能超过1M');
            this.$refs.uploadEl.value = '';
            return;
        }
        let data = {
            username: this.user.username,
            file: file,
            type: file.type.split('/')[1]
        };
        socket.emit('uploadUserIcon', data);
        this.$refs.uploadEl.value = '';
    }
    socket.on('uploadUserIcon', function(data) {
        let time = new Date().getTime();
        let savePath = `/static/userIcon/${time}.${data.type}`;
        let hostPath = 'http://' + host + ':' + port;
        // 通过fs模块操作
        fs.writeFile('.'+ savePath, data.file, function(err) {
            if (err) {
                console.log(err);
                io.sockets.connected[socket.id].emit('err', 'save userIcon  failed');
            } else {
                userModel.update({username: data.username}, {$set: {userIcon: hostPath + savePath}}).then(res => {
                    userModel.findOne({username: data.username}).then(user=>{
                        io.sockets.connected[socket.id].emit('uploadUserIcon', {
                            user: user,
                            message: 'upload userIcon success'
                        });
                    }).catch(err =>{
                        io.sockets.connected[socket.id].emit('err', 'find userInfo failed');
                    });
                }).catch(err => {
                    io.sockets.connected[socket.id].emit('err', 'save userIcon path failed');
                })
            }
        })
    });

    登录注册

    将用户名作为唯一值。注册时不能注册已存在的用户名。登录支持自动登录,将密码保存在localStorage中。

    待处理bug以及优化

    打包后静态资源路径有问题(有没有大神能帮帮我QAQ)

    需要未读消息小红点

    增加表情、图片发送

    最后: 这是本菜鸡陆陆续续做了一年的项目,多次放弃又重新拾起。代码写得不堪入目,没有精力和激情再去做优化了。暂时先这样吧......

  • 相关阅读:
    NSLocalizedString用法
    4-27学习心得
    手势学习
    plist处理
    数据存储
    initWithFrame方法
    控制器跳转小常识
    UIGestureRecognizer学习笔记
    博客资源
    检测手机wifi有没有打开
  • 原文地址:https://www.cnblogs.com/10manongit/p/12823268.html
Copyright © 2020-2023  润新知