一个完整的项目基本分为三个部分:前端、后台和数据库。依照软件工程的理论知识,应该依次按照以下几个步骤:需求分析、概要设计、详细设计、编码、测试等。由于缺乏相关知识的储备,导致这个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" οnsubmit="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" οnsubmit="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" οnsubmit="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     varxhr;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     varxhr;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   varLastr, 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   functionUpdateRoom() {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   functionUL() {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 === '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     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 === '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 === '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 === '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   functionscroll() {428     $('#messages,#oldMsg ul').animate({429       scrollTop: 999999999
430     }, 0);431 }432
433   /*
434 *删除cookie435   */
436   functionclearCookie() {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   functiongetCookie(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   functionrender() {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   functionreplace_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="img/$1.gif" border="0" />');484     returnstr;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   functionfindWeather() {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/" +_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=newmongoose.Schema({3 name:{4 type:String,5      index:true,6 },7 roomName:{8 type:String9 },10 msg:{11 type:String,12 },13 saytime:{14 type:String,15 }16 });17 var UserSchema = newmongoose.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.now33 },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: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 functiongettime() {6   var time = newDate();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 inrooms) {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 = newMsg({ 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 inrooms) {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     functiontotal() {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', functioncallback() {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

转载于:https://www.cnblogs.com/jiangcheng-langzi/p/7074441.html

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

  1. ComeFuture英伽学院——2020年 全国大学生英语竞赛【C类初赛真题解析】(持续更新)

    视频:ComeFuture英伽学院--2019年 全国大学生英语竞赛[C类初赛真题解析]大小作文--详细解析 课件:[课件]2019年大学生英语竞赛C类初赛.pdf 视频:2020年全国大学生英语竞赛 ...

  2. ComeFuture英伽学院——2019年 全国大学生英语竞赛【C类初赛真题解析】大小作文——详细解析

    视频:ComeFuture英伽学院--2019年 全国大学生英语竞赛[C类初赛真题解析]大小作文--详细解析 课件:[课件]2019年大学生英语竞赛C类初赛.pdf 视频:2020年全国大学生英语竞赛 ...

  3. 信息学奥赛真题解析(玩具谜题)

    玩具谜题(2016年信息学奥赛提高组真题) 题目描述 小南有一套可爱的玩具小人, 它们各有不同的职业.有一天, 这些玩具小人把小南的眼镜藏了起来.小南发现玩具小人们围成了一个圈,它们有的面朝圈内,有的 ...

  4. 信息学奥赛之初赛 第1轮 讲解(01-08课)

    信息学奥赛之初赛讲解 01 计算机概述 系统基本结构 信息学奥赛之初赛讲解 01 计算机概述 系统基本结构_哔哩哔哩_bilibili 信息学奥赛之初赛讲解 02 软件系统 计算机语言 进制转换 信息 ...

  5. 信息学奥赛一本通习题答案(五)

    最近在给小学生做C++的入门培训,用的教程是信息学奥赛一本通,刷题网址 http://ybt.ssoier.cn:8088/index.php 现将部分习题的答案放在博客上,希望能给其他有需要的人带来 ...

  6. 信息学奥赛一本通习题答案(三)

    最近在给小学生做C++的入门培训,用的教程是信息学奥赛一本通,刷题网址 http://ybt.ssoier.cn:8088/index.php 现将部分习题的答案放在博客上,希望能给其他有需要的人带来 ...

  7. 信息学奥赛一本通 提高篇 第六部分 数学基础 相关的真题

    第1章   快速幂 1875:[13NOIP提高组]转圈游戏 信息学奥赛一本通(C++版)在线评测系统 第2 章  素数 第 3 章  约数 第 4 章  同余问题 第 5 章  矩阵乘法 第 6 章 ...

  8. 信息学奥赛一本通题目代码(非题库)

    为了完善自己学c++,很多人都去读相关文献,就比如<信息学奥赛一本通>,可又对题目无从下手,从今天开始,我将把书上的题目一 一的解析下来,可以做参考,如果有错,可以告诉我,将在下次解析里重 ...

  9. 信息学奥赛一本通(C++版) 刷题 记录

    总目录详见:https://blog.csdn.net/mrcrack/article/details/86501716 信息学奥赛一本通(C++版) 刷题 记录 http://ybt.ssoier. ...

  10. 最近公共祖先三种算法详解 + 模板题 建议新手收藏 例题: 信息学奥赛一本通 祖孙询问 距离

    首先什么是最近公共祖先?? 如图:红色节点的祖先为红色的1, 2, 3. 绿色节点的祖先为绿色的1, 2, 3, 4. 他们的最近公共祖先即他们最先相交的地方,如在上图中黄色的点就是他们的最近公共祖先 ...

最新文章

  1. jquery sortable简述
  2. C# System.Runtime.InteropServices 相关学习总结
  3. CodeForces - 1304D Shortest and Longest LIS(构造+贪心)
  4. php is_null(,PHP empty() isset() is_null() 区别与性能比较
  5. 上班族如何当老板 五大模式任你选
  6. python画烟花的代码_电脑怎么用代码画烟花?
  7. sps 2007 小问题1:人员选择器PeopleEditor如何使用
  8. 同一主机的多个子进程使用同一个套接字_如何在Go语言中使用Websockets:最佳工具与行动指南...
  9. Android Bitmap保存时背景变为黑色的问题
  10. 经济学原理——微观经济学
  11. 吉客云与金蝶云星空集成方案(吉客云主管库存)
  12. ffmpeg命令分析-b:v
  13. python-tkinter使用方法——转载(二)
  14. ORACLE RAC 一节点出现Suspending MMON slave action kewrmrfsa_ for 82800 seconds
  15. (SCI论文写作)三种高效的论文用公式编辑器推荐(Word、mathtype、亿图公式编辑器)
  16. 【Python 字符视频】Python 实现将抖音视频转换成字符视频
  17. Codeforces Round #644 (Div. 3) E.Polygon
  18. 咸鱼ZTMR实例—板载LED
  19. 商城类小程序开发全过程(附源码)
  20. Q4财报一如既往增长稳健 陌陌为何能逆势增长?

热门文章

  1. Yii2之gii的配置与使用
  2. 货币政策的目标、工具、中性利率、货币乘数及对宏观经济的影响分析
  3. 中式台球 规则 ( ChinaBilliards )
  4. 帕累托最优(Pareto Optimality)和纳什均衡(Nash Equilibrium)之间的关系
  5. quill自定义清除格式功能,选中内容清除格式
  6. CoppeliaSim(原Vrep)中实现多关节机械臂的逆运动学仿真(tar-tip)
  7. pxe clonezilla
  8. 2020年最佳英文关键词研究工具!
  9. Unity实现模型显示在UI前面
  10. Spring框架之工厂模式