首页 > 代码库 > 《基于Node.js实现简易聊天室系列之详细设计》

《基于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="http://www.mamicode.com/img/firsthead.jpg"  /><span>The Legend of Qin</span></li> 80               <li><img src="http://www.mamicode.com/img/secondhead.jpg" ><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="http://www.mamicode.com/socket.io/socket.io.js"></script>138   <script src="http://www.mamicode.com/js/jquery.js"></script>139   <script src="http://www.mamicode.com/js/jquery-ui.min.js"></script>140   <script src="http://www.mamicode.com/js/app.js"></script>141   <script src="http://www.mamicode.com/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 =http://www.mamicode.com/== ‘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 =http://www.mamicode.com/== ‘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="http://www.mamicode.com/‘ + CookieObj.h_imgPath + ‘"/>‘ + ‘</li>‘; 89     } else { 90       str = ‘<li class="Lileft"><img class="msgImg" src="http://www.mamicode.com/‘ + 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="http://www.mamicode.com/‘ + 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="http://www.mamicode.com/‘ + 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="http://www.mamicode.com/‘ + 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.name214         }215       })216         .done(function (data, textStatus, jqXHR) {217           if (data.value =http://www.mamicode.com/== ‘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="http://www.mamicode.com/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     width: 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: selectedImg299           }300         }).done(function (data) {301           if (data.value =http://www.mamicode.com/== ‘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: $newSign329             }330           }).done(function (data) {331             if (data.value =http://www.mamicode.com/== ‘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: $newpass364             }365           }).done(function (data, textStatus, jqXHR) {366             if (data.value =http://www.mamicode.com/== ‘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 =http://www.mamicode.com/== ‘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: 999999999430     }, 0);431   }432 433   /*434   *删除cookie435   */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   *获取cookie448   */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    *界面render461   */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(/\n/g, ‘<br/>‘);483     str = str.replace(/\[em_([0-9]*)\]/g, ‘<img src="http://www.mamicode.com/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 =http://www.mamicode.com/== ‘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://www.mamicode.com/‘http://i2.sinaimg.cn/dy/main/weather/weatherplugin/wthIco/20_20/" + _f562             + "‘ />";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: true22   },23   password:{24     type: String,25     index: true26   },27   user_id: {28     type: mongoose.Schema.Types.ObjectId,29     index: true30   },31   updated: {32     type: Date, default: Date.now33   },34   status: {35     type: Boolean,36     default: false37   },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:Msg53 };
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

 

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