• 《基于Node.js实现简易聊天室系列之详细设计》


    一个完整的项目基本分为三个部分:前端、后台和数据库。依照软件工程的理论知识,应该依次按照以下几个步骤:需求分析、概要设计、详细设计、编码、测试等。由于缺乏相关知识的储备,导致这个Demo系列的文章层次不是很清楚,索性这一章将所有的过程(前后端以及数据库)做一个介绍,下一章写完总结就OK了吧。

    (1)前端部分

    涉及到的技术:htmlcssbootstrapjqueryjquery UI

    登录/注册界面使用的是bootstrap响应式布局,即支持不同尺寸的客户端,以此提高用户的体验。在这之前我以为聊天室比较适合做成SPA(单页应用),想采取backbone,但是结合毕设的主题是基于Node.js,如果采用backbone,路由功能就有两种选择,backboneNode.js都有着丰富的路由API,由于之前没有用Node.js做过相关项目,所以就放弃了backboneDemo通过改变cssdisplay的属性来控制div的显示与隐藏。

    页面:

      1 <div>
      2     <div class="container">
      3       <div class="row">
      4         <div class="col-sm-5 col-md-5">
      5           <div id="loginBox">
      6             <form id="signinForm" class="form-signin" role="form" onsubmit="return false;">
      7               <h2 class="form-signin-heading">Sign in</h2>
      8               <input id="username" type="text" class="form-control" placeholder="Username" required="" autofocus="">
      9               <input id="userpassword" type="password" class="form-control" placeholder="Password" required="">
     10               <button id="loginBtn" class="btn btn-lg btn-primary btn-block">Sign in</button>
     11             </form>
     12             <p id="SignInErr"></p>
     13           </div>
     14         </div>
     15         <div class="col-sm-2 col-md-2">
     16           <div class="text-center"><br><br>
     17             <h1>Or</h1>
     18           </div>
     19         </div>
     20         <div class="col-sm-5 col-md-5">
     21           <div id="signupBox">
     22             <form id="signupForm" class="form-signin" role="form" onsubmit="return false;">
     23               <h2 class="form-signin-heading">Sign up</h2>
     24               <input id="upName" type="text" maxlength="5" class="form-control" placeholder="Username" required="" />
     25               <input id="upPassword" type="password" maxlength="6" class="form-control" placeholder="Password" required="" />
     26               <button id="signupBtn" class="btn btn-lg btn-primary btn-block">Sign up</button>
     27             </form>
     28             <p id="SignUpErr"></p>
     29           </div>
     30         </div>
     31       </div>
     32     </div>
     33     <div id="main" class="hidden">
     34       <div id="sideBar">
     35         <div id="userInfo">
     36           <img class="headImg" />
     37           <span id="weather"></span>
     38         </div>
     39         <hr style="margin:0;">
     40         <div id="control">
     41           <div>
     42             <span id="gloableName"></span>
     43             <br>
     44             <em></em>
     45           </div>
     46           <hr>
     47           <ul>
     48             <li id="set"><i class="glyphicon glyphicon-cog"></i>&nbsp;&nbsp;Setting</li>
     49             <li id="changeUser"><i class="glyphicon glyphicon-transfer"></i>&nbsp;&nbsp;Switch</li>
     50             <li id="layout"><i class="glyphicon glyphicon-off"></i>&nbsp;&nbsp;Layout</li>
     51           </ul>
     52         </div>
     53         <ul id="setContent" style="display: none">
     54           <li><i class="glyphicon glyphicon-eye-close"></i><em>&nbsp;&nbsp;Update Password</em></li>
     55           <li><i class="glyphicon glyphicon-tags"></i><em>&nbsp;&nbsp;Personal Sign</em></li>
     56           <li><i class="glyphicon glyphicon-user"></i><em>&nbsp;&nbsp;Head Portrait </em></li>
     57         </ul>
     58         <div id="setOne" style="display:none;">
     59           <input type="password" placeholder="Old Password" maxlength="6" id="oldpass" />
     60           <input type="password" placeholder="New Password" maxlength="6" id="newpass" />
     61           <p></p>
     62         </div>
     63         <div id="setTwo" style="display: none;">
     64           <input type="text" placeholder="write something will well" maxlength="16" />
     65           <p></p>
     66         </div>
     67         <div id="setThree" style="display:none;">
     68           <p>*Double click the picture to select</p>
     69           <div id="imgContent">
     70             <ul>
     71             </ul>
     72           </div>
     73         </div>
     74         <div id="chatChange">
     75           <ul id="selectmenu">
     76             <li>Square</li>
     77             <li>Choose Room</li>
     78             <ul id="selectRoom" style="display: none;">
     79               <li><img src="/img/firsthead.jpg" alt="" /><span>The Legend of Qin</span></li>
     80               <li><img src="/img/secondhead.jpg" alt=""><span>Naruto</span></li>
     81             </ul>
     82           </ul>
     83 
     84           <div>
     85           </div>
     86         </div>
     87       </div>
     88       <div id="chatBox">
     89         <div id="headmessages"><strong>Square</strong></div>
     90         <div id="content">
     91           <ul id="messages"></ul>
     92         </div>
     93         <div id="chatbottom">
     94           <div>
     95             <span class="emotion" title="插入表情"><i class="glyphicon glyphicon-picture"></i></span>
     96             <span id="clear" title="清空聊天窗口"><i class="glyphicon glyphicon-refresh"></i></span>
     97             <span id="chatRecord" title="聊天历史消息"><i class="glyphicon glyphicon-time"></i></span>
     98           </div>
     99           <form id="chatMsgForm" onsubmit="return false;">
    100             <textarea id="msg" rows="5" cols="35" maxlength="161" placeholder="Enter the content here, you can enter 161 characters at most ~">
    101             </textarea>
    102             <button id="send" class="btn btn-default"><i class="glyphicon glyphicon-send"></i></button>
    103           </form>
    104         </div>
    105       </div>
    106       <div id="model">
    107       </div>
    108       <div id="rightSide">
    109         <span id="membersTitle">Members Information</span>
    110         <div id="Allmembers">
    111           <div>
    112             <i></i>
    113             <span>All Members</span>
    114             <span id="oncount"></span>/<span id="allcount"></span>
    115           </div>
    116           <ul id="AllOnline"></ul>
    117           <ul id="AllOutline"></ul>
    118         </div>
    119         <div id="Roommembers">
    120           <div>
    121             <i></i>
    122             <span>Room Members</span>
    123             <span id="roomCount"></span>
    124           </div>
    125           <ul>
    126           </ul>
    127         </div>
    128       </div>
    129       <div id="oldMsg" style="display: none;">
    130         <span id="oldMsgHead" title="关闭历史消息窗口"><i class="glyphicon glyphicon-arrow-left"></i>&nbsp;&nbsp;MsgHistory</span>
    131         <ul></ul>
    132         <span id="clearoldMsg"><i title="清空聊天历史消息" class="glyphicon glyphicon-trash"></i></span>
    133       </div>
    134     </div>
    135   </div>
    136   </div>
    137   <script src="/socket.io/socket.io.js"></script>
    138   <script src="/js/jquery.js"></script>
    139   <script src="/js/jquery-ui.min.js"></script>
    140   <script src="/js/app.js"></script>
    141   <script src="/js/jquery.qqFace.js"></script>
    View Code

     

    js:

      1 $(function () {
      2   var CookieObj = {}, socket = io(), headInfo = "群聊  (";;
      3   window.onbeforeunload = function (e) {
      4     if (document.cookie) return false;
      5   }
      6   render();
      7   /*
      8   *登录
      9   */
     10   var onLogin = function (e) {
     11     var xhr;
     12     if (!$('#username').val() || !$('#userpassword').val()) return;
     13     xhr = $.ajax({
     14       url: '/login',
     15       type: 'POST',
     16       dataType: 'json',
     17       data: {
     18         name: $('#username').val(),
     19         password: $('#userpassword').val()
     20       }
     21     })
     22       .done(function (data, textStatus, jqXHR) {
     23         if (data.value === 'Y') {
     24           render();
     25         } else {
     26           $('#SignInErr').html(data.msg);
     27         }
     28       })
     29       .fail(function (jqXHR, textStatus, errorThrown) {
     30         $('#SignInErr').html('Error occured! Please try again.');
     31       });
     32   };
     33   /*
     34   *注册
     35   */
     36   var onSignup = function (e) {
     37     var xhr;
     38     if (!$('#upName').val() || !$('#upPassword').val()) return;
     39     xhr = $.ajax({
     40       url: '/signup',
     41       type: 'POST',
     42       dataType: 'json',
     43       data: {
     44         name: $('#upName').val(),
     45         password: $('#upPassword').val()
     46       }
     47     })
     48       .done(function (data, textStatus, jqXHR) {
     49         if (data.value === 'Y') {
     50           $('#SignUpErr').html(data.msg || 'Login now with these credentials.');
     51         } else {
     52           $('#SignUpErr').html(data.msg || 'Invalid username');
     53         }
     54       })
     55       .fail(function (jqXHR, textStatus, errorThrown) {
     56         $('#SignUpErr').html('Error occured! Please try again.');
     57       });
     58   };
     59 
     60   var onMsgSubmit = function () {
     61     var str = $("#msg").val();
     62     var sendMsg = replace_em(str);
     63     if (!sendMsg || sendMsg.length > 1261) {
     64       alert("err:内容为空或者内容长度超出限制!")
     65       $('#msg').val('');
     66       return;
     67     }
     68     var roomOf = $("#headmessages strong").html();
     69     socket.emit('chat message', sendMsg, CookieObj.h_imgPath, roomOf);
     70     $('#msg').val('');
     71     return false;
     72   };
     73 
     74   socket.on('sysJoin', function (msg) {
     75     var joinInfo = "";
     76     joinInfo = '<li class="markInfo">' + msg + '</li>';
     77     $(joinInfo).appendTo($("#messages")).animate({ "opacity": 0.5 }, 2000, function () {
     78       $(this).animate({ "opacity": 1 }, 1500, function () {
     79         $(this).animate({ "opacity": 0.3 }, 1000);
     80       });
     81     });
     82     scroll();
     83   });
     84 
     85   socket.on('chat message', function (name, msg, img) {
     86     var str = '';
     87     if (name == CookieObj.name) {
     88       str = '<li class="Liright"><p>' + msg + '</p><img class="msgImg" src="' + CookieObj.h_imgPath + '"/>' + '</li>';
     89     } else {
     90       str = '<li class="Lileft"><img class="msgImg" src="' + img + '"/><p>' + msg + '</p></li>';
     91     }
     92     $('#messages').append(str);
     93     scroll();
     94   });
     95 
     96   /*房间选择*/
     97   //默认是进广场,从其他房间执行如下函数
     98   $("#selectmenu li").eq(0).on("click", function (e) {
     99     e.stopPropagation();
    100     $("#selectRoom").hide();
    101     $("#headmessages strong").html("Square");
    102     socket.emit('join', 'Square', $("#gloableName").html());
    103     $("#messages").empty();
    104   });
    105   $("#selectmenu li").eq(1).on("click", function (e) {
    106     e.stopPropagation();
    107     $("#selectRoom").show();
    108   });
    109   //选择秦时明月或火影忍者房间
    110   $("#selectRoom li").on("click", function () {
    111     var roomName = $(this).children("span").html();
    112     var userName = $("#gloableName").html();
    113     $("#headmessages strong").html(roomName);
    114     socket.emit('join', roomName, userName);
    115     $("#messages").empty();
    116   });
    117 
    118 
    119 
    120   /*
    121   *接收所有已注册用户的信息
    122   */
    123   socket.on('onlineUser', function (online) {
    124     var onlineStr = '';
    125     for (var i = 0; i < online.length; i++) {
    126       var item = online[i];
    127       onlineStr += '<li><img src="' + item.h_imgPath + '"/><strong>' + item.name + '</strong><em>[Online]</em></li>';
    128     }
    129     $("#AllOnline").empty();
    130     $("#oncount").html(online.length);
    131     $("#AllOnline").append(onlineStr);
    132   });
    133   socket.on('outlineUser', function (outline) {
    134     var outlineStr = '';
    135     for (var i = 0; i < outline.length; i++) {
    136       var item = outline[i];
    137       outlineStr += '<li><img src="' + item.h_imgPath + '"/><strong>' + item.name + '</strong><em>[Outline]</em></li>';
    138     }
    139     $("#AllOutline").empty();
    140     $("#AllOutline").append(outlineStr);
    141   });
    142   socket.on('allUser', function (doc) {
    143     $('#allcount').html(doc.length);
    144   });
    145   socket.on('disconnect', function (name, msg) {
    146     var leftInfo = "";
    147     leftInfo = '<li class="markInfo leave">' + msg + '</li>';
    148     $(leftInfo).appendTo($("#messages")).animate({ "opacity": 0.3 }, 2000, function () {
    149       $(this).animate({ "opacity": 1 }, 1500, function () {
    150         $(this).animate({ "opacity": 0.3 }, 1000);
    151       });
    152       return this;
    153     });
    154     scroll();
    155   });
    156 
    157   /*当前房间人员信息*/
    158   var Lastr, r1, r2, r3;
    159   socket.on('SquareRoom', function (roomInfo) {
    160     r1 = roomInfo;
    161     UpdateRoom();
    162   });
    163   socket.on('QinRoom', function (roomInfo) {
    164     r2 = roomInfo;
    165     UpdateRoom();
    166   });
    167   socket.on('NarutoRoom', function (roomInfo) {
    168     r3 = roomInfo;
    169     UpdateRoom();
    170   });
    171   function UpdateRoom() {
    172     var $Nowroom = $("#headmessages strong").html(), roomCount, roomStr = '';
    173     switch ($Nowroom) {
    174       case "Square": Lastr = r1; break;
    175       case "The Legend of Qin": Lastr = r2; break;
    176       case "Naruto": Lastr = r3; break;
    177       default: Lastr = r1;
    178     }
    179     roomCount = Lastr.length;
    180     for (var i = 0; i < roomCount; i++) {
    181       var item = Lastr[i];
    182       roomStr += '<li><img src="' + item.h_imgPath + '"/><strong>' + item.name + '</strong><em>[Online]</em></li>';
    183     }
    184     $("#roomCount").html(roomCount);
    185     $("#Roommembers ul").empty();
    186     $("#Roommembers ul").append(roomStr);
    187   }
    188   /*
    189   *切换/退出账号
    190   */
    191   $("#changeUser").on('click', function () {
    192     var res = confirm("Are you sure you want to quit and switch to another account??");
    193     if (res) {
    194       UL();
    195     } else {
    196       $("#control").hide();
    197       $("#setContent").hide();
    198       $("#stateSelect").hide();
    199     }
    200   });
    201   $("#layout").on('click', UL);
    202   function UL() {
    203     if (document.cookie) {
    204       $('#loginDiv').addClass('hidden');
    205       $('#main').removeClass('hidden');
    206       var uname = getCookie("userInfo");
    207       CookieObj = JSON.parse(uname.substr(2));
    208       $.ajax({
    209         url: '/layout',
    210         type: 'POST',
    211         dataType: 'json',
    212         data: {
    213           name: CookieObj.name
    214         }
    215       })
    216         .done(function (data, textStatus, jqXHR) {
    217           if (data.value === 'Y') {
    218             clearCookie();
    219             window.location.reload();
    220           }
    221         });
    222     };
    223   }
    224 
    225   $('#signinForm #loginBtn').click(onLogin);
    226   $('#signupForm #signupBtn').click(onSignup);
    227   $('#chatMsgForm #send').click(onMsgSubmit);
    228 
    229   $("#clear").on("click", function () {
    230     $('#messages').empty();
    231   });
    232 
    233   /*
    234   *监听滚动条事件
    235   */
    236   $('#messages').get(0).onscroll = function () {
    237     $("#messages .Liright").css("margin-right", 1);
    238   }
    239 
    240   /*
    241   *屏蔽回车键
    242   */
    243   $(document).keydown(function (event) {
    244     switch (event.keyCode) {
    245       case 13: return false;
    246     }
    247   });
    248   /*
    249   *用户信息
    250   */
    251   $(".headImg").eq(0).on('click', function (e) {
    252     e.stopPropagation();
    253     if ($("#control").get(0).style.display == "none") {
    254       $("#control").show();
    255     } else {
    256       $("#control").hide();
    257       $("#setContent").hide();
    258       $("#stateSelect").hide();
    259     }
    260   });
    261 
    262   /*更改资料*/
    263   $("#set").on("click", function () {
    264     $("#setContent").show();
    265     $("#stateSelect").hide();
    266   });
    267   /*构造头像选择内容*/
    268   var imgStr = '';
    269   for (var i = 1; i <= 18; i++) {
    270     imgStr += '<li><img data-in="' + i + '" src="./img/' + i + '.jpg"/></li>';
    271     if (i % 6 == 0) {
    272       imgStr += "<br/>";
    273     }
    274   }
    275   $("#setThree #imgContent ul").eq(0).append(imgStr);
    276   $("#setThree #imgContent li img").on("click", function (e) {
    277     e.stopPropagation();
    278     var $index = $(this).attr("data-in");
    279     $("#setThree #imgContent img").removeClass("imgSelected");
    280     $("#setThree #imgContent img").eq(($index - 1)).addClass("imgSelected");
    281   });
    282   /*人物头像模态框*/
    283   $("#setThree").dialog({
    284     autoOpen: false,
    285     title: "Changing Avatar",
    286     modal: true,
    287      578,
    288     resizable: false,
    289     buttons: {
    290       "Ok": function () {
    291         var selectedImg = $(".imgSelected").attr("data-in");
    292         //  alert(selectedImg);
    293         $.ajax({
    294           url: "/updateImg",
    295           type: "POST",
    296           data: {
    297             name: $('#control div span').eq(0).html(),
    298             imgIndex: selectedImg
    299           }
    300         }).done(function (data) {
    301           if (data.value === 'Y') {
    302             $("#setThree").dialog("close");
    303             $('.headImg').eq(0).attr('src', '/img/' + selectedImg + '.jpg');
    304             $('#setContent').hide();
    305             $('#control').hide();
    306             // alert(data.msg);
    307           }
    308         });
    309         ;
    310       }
    311     }
    312   });
    313   /*个性签名模态框*/
    314   $("#setTwo").dialog({
    315     autoOpen: false,
    316     title: "Personalized signature setting",
    317     modal: true,
    318     resizable: false,
    319     buttons: {
    320       "OK": function () {
    321         var $newSign = $("#setTwo input[type='text']").eq(0).val();
    322         if ($newSign != '') {
    323           $.ajax({
    324             url: '/updateSign',
    325             type: 'POST',
    326             data: {
    327               name: $('#control div span').eq(0).html(),
    328               newSign: $newSign
    329             }
    330           }).done(function (data) {
    331             if (data.value === 'Y') {
    332               $("#setTwo p").eq(0).html(data.msg);
    333               setTimeout(function () {
    334                 $('#control div em').eq(0).html($newSign);
    335                 $("#setTwo").dialog('close');
    336                 $("#setTwo p").eq(0).html('');
    337               }, 1000);
    338             }
    339           });
    340         }
    341       },
    342       "Cancel": function () {
    343         $(this).dialog('close');
    344       }
    345     }
    346   });
    347   /*密码模态框*/
    348   $("#setOne").dialog({
    349     autoOpen: false,
    350     title: "Changeing User password",
    351     modal: true,
    352     resizable: false,
    353     buttons: {
    354       "Ok": function () {
    355         var $oldpass = $("#setOne #oldpass").val(), $newpass = $("#setOne #newpass").val();
    356         if ($oldpass != '' && $newpass != '') {
    357           $.ajax({
    358             url: '/changepass',
    359             type: 'POST',
    360             data: {
    361               name: $('#control div span').eq(0).html(),
    362               oldpass: $oldpass,
    363               newpass: $newpass
    364             }
    365           }).done(function (data, textStatus, jqXHR) {
    366             if (data.value === 'Y') {
    367               $("#setOne p").eq(0).html(data.msg);
    368               setTimeout(function () {
    369                 clearCookie();
    370                 $("#setOne p").eq(0).html('');
    371                 window.location.reload();
    372               }, 1000);
    373             } else if (data.value === 'N') {
    374               $("#setOne p").eq(0).html(data.msg);
    375               $("#setOne #oldpass").val('');
    376               $("#setOne #newpass").val('');
    377             }
    378           });
    379         }
    380       },
    381       "Cancel": function () {
    382         $(this).dialog('close');
    383       }
    384     }
    385   });
    386   $("#setContent li").eq(0).click(function (e) {
    387     e.stopPropagation();
    388     $("#setOne").dialog("open");
    389   });
    390   $("#setContent li").eq(1).click(function (e) {
    391     e.stopPropagation();
    392     $("#setTwo").dialog("open");
    393   });
    394   $("#setContent li").eq(2).click(function (e) {
    395     e.stopPropagation();
    396     $("#setThree").dialog("open");
    397   });
    398 
    399   /*
    400   *成员信息面板控制:包括所有成员和具体房间成员的状态
    401   */
    402   $("#rightSide #Roommembers ul").hide();
    403   $("#Allmembers div").css({ 'backgroundColor': "rgb(70,130,180)", "color": "white" });
    404   $("#Allmembers div i").addClass("glyphicon glyphicon-triangle-bottom");
    405   $("#Roommembers div i").addClass("glyphicon glyphicon-triangle-right");
    406   $("#rightSide div >div").click(function (e) {
    407     var $title = $(this), $anotherTitle = $(this).parent().siblings("div");
    408     if ($title.next('ul').is(":visible")) {
    409       $title.siblings('ul').hide();
    410       $title.children("i").removeClass("glyphicon glyphicon-triangle-bottom").addClass("glyphicon glyphicon-triangle-right");
    411       $title.css({ 'backgroundColor': "", "color": "" });
    412     } else {
    413       $anotherTitle.children('ul').hide();
    414       $anotherTitle.children('div').css({ 'backgroundColor': "", "color": "" });
    415       $anotherTitle.children("div").children("i").removeClass("glyphicon glyphicon-triangle-bottom").addClass("glyphicon glyphicon-triangle-right");
    416       $title.css({ 'backgroundColor': "rgb(70,130,180)", "color": "white" });
    417       $title.siblings('ul').slideToggle(500).show();
    418       $title.children("i").removeClass("glyphicon glyphicon-triangle-right").addClass("glyphicon glyphicon-triangle-bottom");
    419     }
    420   });
    421 
    422   /*函数集*/
    423 
    424   /*
    425   *保证scroll始终在最底端
    426   */
    427   function scroll() {
    428     $('#messages,#oldMsg ul').animate({
    429       scrollTop: 999999999
    430     }, 0);
    431   }
    432 
    433   /*
    434   *删除cookie
    435   */
    436   function clearCookie() {
    437     var keys = document.cookie.match(/[^=;]+(?==)/g);
    438     if (keys) {
    439       var i = keys.length;
    440       while (i--) {
    441         document.cookie = keys[i] + '=0;expires=' + new Date(0).toUTCString();
    442       }
    443     }
    444   }
    445 
    446   /*
    447   *获取cookie
    448   */
    449   function getCookie(sname) {
    450     var aCoookie = document.cookie.split(";");
    451     for (var i = 0; i < aCoookie.length; i++) {
    452       var aCrumb = aCoookie[i].split("=");
    453       if (sname == aCrumb[0])
    454         return decodeURIComponent(aCrumb[1]);
    455     }
    456     return null;
    457   }
    458 
    459   /*
    460    *界面render
    461   */
    462   function render() {
    463     if (document.cookie) {
    464       $('.container').addClass('hidden');
    465       $('#main').removeClass('hidden');
    466       var uname = getCookie("userInfo");
    467       CookieObj = JSON.parse(uname.substr(2));
    468       socket.emit('join', $("#headmessages strong").html(), CookieObj.name);
    469       $('.headImg').eq(0).attr('src', CookieObj.h_imgPath);
    470       $('#control div span').eq(0).html(CookieObj.name);
    471       $('#control div em').eq(0).html(CookieObj.personalizedSign);
    472     };
    473   }
    474   $('.emotion').qqFace({
    475     id: 'facebox',
    476     assign: 'msg',
    477     path: 'img/'    //表情存放的路径
    478   });
    479   function replace_em(str) {
    480     str = str.replace(/</g, '&lt;');
    481     str = str.replace(/>/g, '&gt;');
    482     str = str.replace(/
    /g, '<br/>');
    483     str = str.replace(/[em_([0-9]*)]/g, '<img src="img/$1.gif" border="0" />');
    484     return str;
    485   }
    486   /*查询聊天记录*/
    487   $("#chatRecord").on('click', function () {
    488     $.ajax({
    489       url: "/queryChatMsg",
    490       type: "POST",
    491       data: {
    492         roomName: $("#headmessages strong").html()
    493       }
    494     }).done(function (data) {
    495       var Msg = data.msg;
    496       var msgStr = '',
    497         $name = $('#control div span').eq(0).html();
    498       for (var i = 0; i < Msg.length; i++) {
    499         var item = Msg[i];
    500         if (item.name == $name) {
    501           msgStr += '<li><span class="blue">' + item.name + '</span>&nbsp;&nbsp;<em class="blue">' + item.saytime + '</em><br/><span>' + item.msg + '</span></li>';
    502         } else {
    503           msgStr += '<li><span class="green">' + item.name + '</span>&nbsp;&nbsp;<em class="green">' + item.saytime + '</em><br/><span>' + item.msg + '</span></li>';
    504         }
    505       }
    506       $("#oldMsg ul").empty();
    507       $("#oldMsg ul").css({ "background-img": 'url("/img/loading.gif")' });
    508       $("#rightSide").hide();
    509       $("#oldMsg").show();
    510       setTimeout(function () {
    511         $("#oldMsg ul").append(msgStr);
    512         scroll();
    513       }, 2000);
    514     });
    515   });
    516 
    517   /*关闭历史记录窗口*/
    518   $("#oldMsgHead i").on('click', function () {
    519     $("#oldMsg ul").empty();
    520     $("#oldMsg").hide();
    521     $("#rightSide").show();
    522   });
    523   /*清空聊天历史消息*/
    524   $("#clearoldMsg i").on('click', function () {
    525     var result = confirm("This action will delete the chat record on the database. Do you want to continue?");
    526     if (result) {
    527       $.ajax({
    528         url: '/deleteMsg',
    529         type: 'POST',
    530         data: {
    531           roomName: $("#headmessages strong").html()
    532         }
    533       }).done(function (data) {
    534         if (data.value === 'Y') {
    535           alert(data.msg);
    536           $("#oldMsg ul").empty();
    537           $("#oldMsg").hide();
    538           $("#rightSide").show();
    539         }
    540       });
    541     }
    542   });
    543   //多行文本输入框自动聚焦
    544   $("#msg").focus();
    545   //获取当前城市以及城市天气
    546   function findWeather() {
    547     var cityUrl = 'http://int.dpool.sina.com.cn/iplookup/iplookup.php?format=js';
    548     $.getScript(cityUrl, function (script, textStatus, jqXHR) {
    549       var citytq = remote_ip_info.city;// 获取城市
    550       var url = "http://php.weather.sina.com.cn/iframe/index/w_cl.php?code=js&city=" + citytq + "&day=0&dfc=3";
    551       $.ajax({
    552         url: url,
    553         dataType: "script",
    554         scriptCharset: "gbk",
    555         success: function (data) {
    556           var _w = window.SWther.w[citytq][0];
    557           var _f = _w.f1 + "_0.png";
    558           if (new Date().getHours() > 17) {
    559             _f = _w.f2 + "_1.png";
    560           }
    561           var img = "<img width='25px' height='25px' src='http://i2.sinaimg.cn/dy/main/weather/weatherplugin/wthIco/20_20/" + _f
    562             + "' />";
    563           // var tq = citytq + " " + img + " " + _w.s1 + " " + _w.t1 + "℃~" + _w.t2 + "℃ " + _w.d1 + _w.p1 + "级";
    564           var tq = img + _w.s1 + ' ' + citytq + "<br/><span>&nbsp" + _w.t2 + "℃~" + (_w.t1 || 25) + "℃ " + "</span>";
    565           $('#weather').html(tq);
    566         }
    567       });
    568     });
    569   }
    570 
    571   findWeather();
    572 });
    View Code

    主界面如下图所示即聊天分为三个模块(左中右)即:左为功能模块,用户可以进行房间的选择,以及点击自己的图像修改个人资料等操作;中为聊天模块,显示当前房间聊天内容以及聊天信息输入框;右为信息展示模块,默认显示所有在线用户信息以及当前房间在线成员信息,用户可以切换查看当前房间历史聊天记录。

     

    (2)数据库

    涉及到的技术:mongoDB、mongoose 

    由于javascript是一门弱类型语言,所以操作数据库没有javaphp等语言方便。但是我们可以通过mongoose建立模型model映射到数据库中去,将对数据库的操作转换到操作model中去。

     

     1 var mongoose = require("mongoose");
     2 var msgRecord=new mongoose.Schema({
     3     name:{
     4      type:String,
     5      index:true,
     6     },
     7     roomName:{
     8       type:String
     9     },
    10     msg:{
    11       type:String,
    12     },
    13     saytime:{
    14       type:String,
    15     }     
    16 });
    17 var UserSchema = new mongoose.Schema({
    18   name: {
    19     type: String,
    20     unique: true,
    21     index: true
    22   },
    23   password:{
    24     type: String,
    25     index: true
    26   },
    27   user_id: {
    28     type: mongoose.Schema.Types.ObjectId,
    29     index: true
    30   },
    31   updated: {
    32     type: Date, default: Date.now
    33   },
    34   status: {
    35     type: Boolean,
    36     default: false
    37   },
    38   h_imgPath: {
    39     type: String,
    40     default:"/img/1.jpg"
    41   },
    42   personalizedSign:{
    43     type:String,
    44     default:"Write something will well`"
    45   }
    46 });
    47 
    48 var User = mongoose.model('User', UserSchema);
    49 var Msg=mongoose.model('Msg',msgRecord);
    50 module.exports = {
    51   User:User,
    52   Msg:Msg
    53 };
    View Code

    (3)后台

           涉及到的技术:Node.js,socket.io,Express

    后台作为前端和数据库的桥梁,接收前端传过来的参数,去请求服务器,响应不同的服务请求。同时,通过socket.io进行实时通信,实时通信的前提是在客户端也要引入相关的js文件,通过on()emit()方法、自定义事件达到目的

    操作socket.io

      1 var users = {};
      2 var QueryUser = require('./mongoDB/models/model').User;
      3 var Msg = require('./mongoDB/models/model').Msg;
      4 //获取实时时间
      5 function gettime() {
      6   var time = new Date();
      7   var timepartone = time.getFullYear() + '-' + (time.getMonth() + 1) + '-' + time.getDate() + ' ';
      8   var timemid = time.getHours(), s;
      9   if (timemid < 6) {
     10     s = "凌晨 " + timemid;
     11   } else if (timemid < 12) {
     12     s = "上午 " + timemid;
     13   } else if (timemid < 18) {
     14     s = "下午 " + '0' + (timemid - 12);
     15   } else {
     16     s = "晚上 " + (timemid - 12);
     17   }
     18   var timeparttwo = s + ":" + (time.getMinutes() < 10 ? '0' + time.getMinutes() : time.getMinutes());
     19   return timepartone + timeparttwo;
     20 }
     21 /*创建三个房间:Square、The Legend of Qin、Naruto*/
     22 var rooms = { 'Square': [], 'The Legend of Qin': [], 'Naruto': [] };
     23 var user = '';
     24 module.exports = function (app, io) {
     25   io.on('connection', function (socket) {
     26     socket.on('join', function (roomName, userName) {
     27       user = userName;
     28       users[socket.id] = userName;
     29       for (var i in rooms) {
     30         if (roomName != i) {
     31           var index = rooms[i].indexOf(user);
     32           if (index !== -1) {
     33             console.log("删除前" + rooms[i]);
     34             rooms[i].splice(index, 1);
     35             io.to(i).emit('sysLeft', user + "退出了房间" + roomName);
     36             socket.leave(i);
     37             console.log(userName + '离开了房间' + i + ':这个房间里还有' + rooms[i]);
     38           }
     39         }
     40       }
     41       var flag = true;
     42       for (var j = 0; j < rooms[roomName].length; j++) {
     43         if (rooms[roomName][j] == user) {
     44           flag = false;
     45         }
     46       }
     47       if (flag) {
     48         rooms[roomName].push(user);
     49         socket.join(roomName);
     50       }
     51       io.sockets.in(roomName).emit('sysJoin', user + '加入了房间' + roomName);
     52       total();
     53       console.log(user + '加入了' + roomName);
     54     });
     55     socket.on('chat message', function (msg, img, roomOf) {
     56       var name = '';
     57       name = users[socket.id];
     58       var newMsg = new Msg({ name: name, msg: msg, saytime: gettime(),roomName:roomOf });
     59       newMsg.save();
     60       if (rooms[roomOf].indexOf(name) === -1) {
     61         return false;
     62       }
     63       console.log(roomOf + ":" + msg);
     64       io.sockets.in(roomOf).emit('chat message', name, msg, img);
     65     });
     66     socket.on('disconnect', function () {
     67       var msg = '', name = '', time = '';
     68       time = gettime();;
     69       name = users[socket.id];
     70       for (var i in rooms) {
     71           var index = rooms[i].indexOf(name);
     72           if (index !== -1) {
     73             console.log("删除前" + rooms[i]);
     74             rooms[i].splice(index, 1);
     75             io.to(i).emit('sysLeft', name + "退出了房间" + i);
     76             socket.leave(i);
     77             console.log(name + '离开了房间' + i + ':这个房间里还有' + rooms[i]);
     78         }
     79       }
     80       msg = name + '离开群聊  ' + time;
     81       io.emit('disconnect', name, msg);
     82       var timeTotal = total();
     83     });
     84     //获取总用户
     85     function total() {
     86       QueryUser.find({}, function (err, doc) {
     87         io.emit('allUser', doc);
     88       });
     89       QueryUser.find({ status: false }, function (err, doc) {
     90         io.emit('outlineUser', doc);
     91       });
     92       QueryUser.find({ status: true }, function (err, doc) {
     93         io.emit('onlineUser', doc);
     94       });
     95       //查询房间里成员的信息
     96       /*三个房间:Square、The Legend of Qin、Naruto*/
     97       var F_RMInfo = [], S_RMInfo = [], T_RMInfo = [];
     98       for (var k = 0; k < rooms["Square"].length; k++) {
     99         QueryUser.findOne({ name: rooms["Square"][k] }, function (err, doc) {
    100           F_RMInfo.push(doc);
    101           io.sockets.in("Square").emit('SquareRoom', F_RMInfo);
    102           console.log(F_RMInfo);
    103         });
    104       }
    105       for (var i = 0; i < rooms["The Legend of Qin"].length; i++) {
    106         QueryUser.findOne({ name: rooms["The Legend of Qin"][i] }, function (err, doc) {
    107           S_RMInfo.push(doc);
    108           io.sockets.in("The Legend of Qin").emit('QinRoom', S_RMInfo);
    109           console.log(S_RMInfo);
    110         });
    111       }
    112       for (var j = 0; j < rooms["Naruto"].length; j++) {
    113         QueryUser.findOne({ name: rooms["Naruto"][j] }, function (err, doc) {
    114           T_RMInfo.push(doc);
    115           io.sockets.in("Naruto").emit('NarutoRoom', T_RMInfo);
    116           console.log(T_RMInfo);
    117         });
    118       }
    119     }
    120   });
    121 
    122 };
    View Code

    服务器js

     1 var express = require('express'),
     2   cookieParser = require('cookie-parser'),
     3   bodyParser = require('body-parser'),
     4   http = require('http'),
     5   path = require('path'),
     6   io = require('socket.io'),
     7   mongoose = require('mongoose'),
     8   app = express(),
     9   db,
    10   userRoutes,
    11   socketIO;
    12 
    13 /* 数据库连接 */
    14 mongoose.connect('mongodb://localhost:27017/chatroom');
    15 db = mongoose.connection;
    16 db.on('error', console.error.bind(console, '数据库连接失败!'));
    17 db.once('open', function callback() {
    18   console.log('数据库连接成功!');
    19 });
    20 
    21 /*Express 配置*/
    22 app.use(cookieParser());
    23 app.use(bodyParser.json()); 
    24 app.use(bodyParser.urlencoded({ extended: true }));
    25 app.use(express.static(path.join(__dirname, 'public')));
    26 
    27 
    28 http=http.createServer(app,function(req,res){
    29   res.writeHead(200, {'Content-Type': 'text/html;charset=utf-8'});
    30 });
    31 io = io(http);
    32 
    33 indexRoutes = require('./routes/index')(app);
    34 userRoutes = require('./routes/users')(app);
    35 
    36 /*绑定io到服务器上*/
    37 socketIO = require('./socketIO')(app, io);
    38 
    39 http.listen(3000, function () {
    40   console.log('listening on *:3000');
    41 });
    View Code
  • 相关阅读:
    推荐19个很有用的 JavaScript 库
    李开复:我对年轻人是分享经验 不是要当导师
    DotNET企业架构应用实践数据库表记录的唯一性设计的设计兼议主键设定原则
    cookies,session,viewstate浅析
    不是HR,Leader你到底需要招什么样的程序员(变形金刚?超人?可能吗!)
    IBatis.Net学习笔记系列文章
    学习mvc的一些资料
    数据库日常维护常用的脚本部分收录
    设定Grid行的颜色
    被WSS3.0耍了一把
  • 原文地址:https://www.cnblogs.com/jiangcheng-langzi/p/7074441.html
Copyright © 2020-2023  润新知