Node.js(Express4.x)搭建聊天室3——完善网页

0.目标与前置条件

这一节,我将完全实现聊天室的全部页面显示和响应。

Node.js(Express4.x)搭建聊天室3——完善网页_第1张图片
Socket.io聊天室Demo

这一节内容是在上两节完成的情况下进行的,请先参照第一节完成基本框架的搭建:
Node.js(Express4.x)搭建聊天室1——基本框架

并参照第二节添加几个监听:
Node.js(Express4.x)搭建聊天室2——消息发送与监听


我把搭建聊天室的步骤分成了几个部分,请按顺序阅读:

获取代码
Node.js(Express4.x)搭建聊天室1——基本框架
Node.js(Express4.x)搭建聊天室2——消息发送与监听
Node.js(Express4.x)搭建聊天室3——完善网页


1.服务端

1.1 chatroom.js

在之前的几节中,我们已经搭建了chatroom的简易版,但如果进入的用户昵称重复了,我们也不能作出判断和处理;此外,当用户修改昵称时,也有可能出现用户昵称重复的情况。(在上一节,我把它定义为了一个对象)

所以,在这一节,我增加了一个数组来存储用户昵称。

var userlist = new Array();

在用户加入聊天时,将昵称存入该数组,如果用户的昵称已存在,则在此昵称后增加一个随机数来保证昵称不同。

  /* *************** 用户emit消息"join"时,响应 *************** */
  socket.on('join', function (username) {
    if (addedUser) return;
          
    // 用户信息存储在socket会话中:在此之前,要检查是否重复
    for(var i=0; i

在用户修改昵称时,在上一节是直接将socket.name替换为新的昵称的。而现在,首先检查数组中是否存在这个昵称,如果没有,则替换,否则提示用户修改失败。

/* *************** 更改昵称 *************** */
  socket.on('change_name', function (newname) {
    if (addedUser) {
        var oldname = socket.username;
        
        // ************************** 这里开始本节更新 ************************** 
        for(var i=0; i

此外,为了维护昵称数组,还需要在用户离开时,将离开的用户剔除出昵称数组。为了达到这个目的,我增加了一个函数来实现:

// 移除数组元素
var removeArr = function(arr, ele) {
    var new_arr = new Array();
    for(var i=0; i

用户离开聊天室:

/* *************** 用户离开 *************** */
  socket.on('disconnect', function () {

      ...

      // 将离开的用户昵称移出数组  
      userlist = removeArr(userlist, socket.username)

      // 告知所有用户
      ...

    }
  });

1.2 路由

在上一节,我们直接就在index页面进行操作了。这一节,我把index界面改为了一个输入用户昵称的界面,然后跳转到一个新界面other。要在routes/index.js中增加一个路由:

router.get('/other', function(req, res, next) {
  res.render('other', { title: 'Express' });
});

2. 客户端

2.1 更改index.jade页面

doctype html
html
    head
        title= title
        link(rel='stylesheet', href='/stylesheets/style.css')
    body
        h1 欢迎使用socket.io聊天室
        form(method='get' action='/other')
            input(id='name' name='name' placeholder='输入您的名字')
            input(type='submit' value='进入聊天室')

2.2 新增other.jade页面

然后在views/index文件夹下创建一个other.jade文件:

doctype html
html
    head
        title= title
        link(rel='stylesheet', href='/stylesheets/style.css')
        link(rel='stylesheet', href='/stylesheets/bootstrap.css')
    body
        h1 socket.io聊天室
        p
            span#status
            span ,
            span#roomstatus
        p#notice.notice
        
        a(href='/')
            [退出] 聊天室
        
        hr
        
        div
            h3 聊天记录
        
        div.scrollbar#msg.msgbox
        
        hr
        div
            textarea(id='msgsend' name='msgsend' placeholder='输入消息' rows='4').form-control
        br
        div
            a.btn.btn-primary(onclick="OL_SendMsg()") 发送
        hr
        form.form-inline
            div.form-group
                input.form-control(id='newnickname' placeholder='新昵称')
                a.btn.btn-danger(onclick="OL_ModifyNickName()") 修改昵称
        
        hr
        h3 系统消息
        div#history
        
    script(src='/javascripts/jquery.min.js')
    script(src='https://cdn.socket.io/socket.io-1.4.5.js')

这里我们引用了一个Bootstrap的css文件,请自行下载,并放入public/stylesheets文件夹中。

另外,我们还需要对css文件进行一下替换:

body {
  padding: 50px;
  font: 14px "Microsoft Yahei", Helvetica, Arial, sans-serif;
}

a {
  color: #00B7FF;
}

.msgbox {
  height:300px; 
  overflow-x:auto; 
  overflow-y:auto; 
  border:1px #ccc solid; 
  border-radius:5px; 
  background:#fff; 
  padding:14px 20px;
}
.notice {
  color:#EF0000; 
  font-weight:bold;
}
.time {
  float:right; 
  color:#999;
}
.mymsg {
  color:#2289DB;
  font-weight:bold;
}

/* 滚动条 */
.scrollbar::-webkit-scrollbar-track
{
  background-color: #e1e1e1;
}
.scrollbar::-webkit-scrollbar
{
  width: 10px;
  background-color: #e1e1e1;
}
.scrollbar.shortscroll::-webkit-scrollbar
{
  width: 8px;
  background-color: #e1e1e1;
}
.scrollbar::-webkit-scrollbar-thumb
{
  background-color: #888;   
}

2.3 other.jade页面的js代码

在other.jade页面中,加入一些js代码。

首先,加入基本功能函数,用于此页面的一些基础功能

script.
        // 基本功能函数
        function ol_pad(num, n)
        { 
            num = ""+num
            var temp = num;
            
            for(var i=0;i<(n-num.length);i++)
            {
                temp = "0"+temp
            }   
            return temp
        }
        function GetRequest() { 
            var url = location.search; //获取url中"?"符后的字串 
            var theRequest = new Object(); 
            if (url.indexOf("?") != -1) { 
                var str = url.substr(1); 
                strs = str.split("&"); 
                for(var i = 0; i < strs.length; i ++) { 
                    theRequest[strs[i].split("=")[0]]=unescape(strs[i].split("=")[1]); 
                } 
            } 
            return theRequest; 
        } 
        function GetDateTime() {
            var obj = new Date();
            return (obj.getFullYear()+"/"+ol_pad(obj.getMonth()+1, 2)+"/"+ol_pad(obj.getDate(), 2)+" "+ol_pad(obj.getHours(),2)+":"+ol_pad(obj.getMinutes(),2)+":"+ol_pad(obj.getSeconds(),2));
        }
        

发送聊天信息后,触发的一些响应,包括发送消息、在聊天框中显示、清空输入框等。

script.
        // 发送聊天信息
        function OL_CleanInput() {
            var obj = document.getElementById('msgsend');
            obj.value = "";
        }
        function OL_ScrollChatWin() {
            var obj = document.getElementById('msg');
            obj.scrollTop = obj.scrollHeight;
        }
        function OL_SentAction() {
            OL_ScrollChatWin();
            OL_CleanInput();
        }
        function OL_CleanNotice() {
            document.getElementById("notice").innerHTML = "";
        }
        function OL_SendMsg() {
            var msg = document.getElementById("msgsend").value;
            if(""==msg) {
                alert("消息不能为空!")
                return -1;
            }
            
            send_msg(msg);
            
            document.getElementById("msg").innerHTML += "

"+G_Name+": "+msg+""+GetDateTime()+"

"; OL_SentAction(); }

修改昵称后的响应

script.
        // 修改昵称
        function OL_ModifyNickName() {
            var newnickname = document.getElementById("newnickname").value;
            if(""==newnickname) {
                alert("新昵称不能为空!")
                return -1;
            }
            
            change_name(newnickname);
            
            document.getElementById("newnickname").value = "";
        }

显示系统公告

script.
        // 通知
        var NoticeTimer = null;
        function OL_ShowNotice(msg, second) {
            NoticeTimer = null;
            document.getElementById("notice").innerHTML = "[消息] "+msg;
            NoticeTimer = setTimeout("OL_CleanNotice()", second*1000)
            
            var history = document.getElementById("history");
            history.innerHTML = "

[消息] "+msg+""+GetDateTime()+"

" + history.innerHTML }

这部分是根据上一节index.jade的socket.io客户端代码进行修改后的内容:

script.
        ////////////////////////////////////////////////////////////////////
        //启动
        var socket = io.connect('http://127.0.0.1:3000');
        
        //发送消息
        var Request = new Object(); 
        Request = GetRequest();     
        var G_Name = Request["name"];
        if(null==G_Name) {
            G_Name = "访客"+Math.ceil(Math.random()*10000);
        }
        socket.emit('join', G_Name, function (data) {
            console.log(data);
        });
        
        //监听
        socket.on('login', function (data) {
            console.log(data);
            // 如果有重名的,要更改一个随机名称
            G_Name = data.username;
            document.getElementById("status").innerHTML = "欢迎您!"+G_Name;
            document.getElementById("roomstatus").innerHTML = "当前聊天有"+data.numUsers+"人";
        });
        
        socket.on('user_joined', function (data) {
            console.log(data);
            OL_ShowNotice(data.msg, 3);
            document.getElementById("roomstatus").innerHTML = "当前聊天有"+data.numUsers+"人";
        });
        
        socket.on('user_left', function (data) {
            console.log(data);
            OL_ShowNotice(data.msg, 3);
            document.getElementById("roomstatus").innerHTML = "当前聊天有"+data.numUsers+"人";
        });
        
        //修改昵称
        function change_name(name){
            socket.emit('change_name', name, function (data) {
                console.log(data);
            });
        }
        // 监听修改昵称后返回的消息
        socket.on('name_changed', function (data) {
            console.log(data);
            document.getElementById("status").innerHTML = "欢迎您!"+G_Name;
            OL_ShowNotice(data.msg, 3);
        });
        // 监听修改昵称后返回给修改者的消息
        socket.on('name_changed_msg', function (data) {
            console.log(data);
            if("success"==data.res) {
                document.getElementById("status").innerHTML = "欢迎您!"+data.newname;
                OL_ShowNotice(data.msg, 3);
            }
            else {
                OL_ShowNotice("修改昵称失败!"+data.error, 3);
            }
        });
        
        //发送消息
        function send_msg(msg){
            socket.emit('send_msg', msg, function (data) {
                console.log(data);
            });
        }
        // 监听消息
        socket.on('msg_sent', function (data) {
            console.log(data);
            
            document.getElementById("msg").innerHTML += "

"+data.username+": "+data.msg+""+GetDateTime()+"

"; OL_ScrollChatWin(); });

3.演示

运行应用(supervisor bin/www 或 node bin/www)

打开两个浏览器,进入127.0.0.1:3000


输入不同的用户昵称后,进入聊天室:

Node.js(Express4.x)搭建聊天室3——完善网页_第2张图片
输入不同昵称

先进入的用户在其他用于进入时,会收到系统公告:

Node.js(Express4.x)搭建聊天室3——完善网页_第3张图片
新用户加入的公告

如果用户昵称与之前的重名,将会:

Node.js(Express4.x)搭建聊天室3——完善网页_第4张图片
昵称重名的情况

用户可以更改昵称,如果成功,会收到提示;其他用户也会通过公告的形式收到提醒。

Node.js(Express4.x)搭建聊天室3——完善网页_第5张图片
更改昵称成功

如果失败,用户会收到提示

Node.js(Express4.x)搭建聊天室3——完善网页_第6张图片
更改昵称失败

用户聊天时,在输入框中输入消息,点击发送后,在聊天记录面板中会有对应的显示。

Node.js(Express4.x)搭建聊天室3——完善网页_第7张图片
Socket.io聊天室Demo

当一个用户离开聊天室了,其他用户会收到消息:

Node.js(Express4.x)搭建聊天室3——完善网页_第8张图片
用户离开聊天室

所有的系统公告会保留在底部:

Node.js(Express4.x)搭建聊天室3——完善网页_第9张图片
系统公告

结语

至此,一个相对饱满一些的聊天室就搭建好了。当然,即使“相对饱满”,依然是很简陋的聊天室。接下来如果要丰满这个聊天室、乃至集成到我们的其他应用中,还是有很多工作可以做的,比如:

  • 支持房间管理。用户可以创建房间,可以选择进入某一个房间
  • 用户管理。用户可以注册帐户、登录帐户,这个涉及到数据库
  • 聊天记录。保存聊天记录
  • 图片、文件发送。允许用户发送图片或其他文件
  • ...

要做好一个聊天室并不容易,但如果我们把它分解成一个个独立的分支,再逐一实现它,就不会那么茫然和不知所措了。

最后,欢迎fork或star我的项目:

https://github.com/KKDestiny/chatroom.git


原创文章,未经许可,请勿转载
作者:Mike的读书季
日期:2016.09.29

你可能感兴趣的:(Node.js(Express4.x)搭建聊天室3——完善网页)