《基于Node.js实现简易聊天室系列之详细设计》
Posted Jinus
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《基于Node.js实现简易聊天室系列之详细设计》相关的知识,希望对你有一定的参考价值。
一个完整的项目基本分为三个部分:前端、后台和数据库。依照软件工程的理论知识,应该依次按照以下几个步骤:需求分析、概要设计、详细设计、编码、测试等。由于缺乏相关知识的储备,导致这个Demo系列的文章层次不是很清楚,索性这一章将所有的过程(前后端以及数据库)做一个介绍,下一章写完总结就OK了吧。
(1)前端部分
涉及到的技术:html、css、bootstrap、jquery、jquery UI
登录/注册界面使用的是bootstrap响应式布局,即支持不同尺寸的客户端,以此提高用户的体验。在这之前我以为聊天室比较适合做成SPA(单页应用),想采取backbone,但是结合毕设的主题是基于Node.js,如果采用backbone,路由功能就有两种选择,backbone和Node.js都有着丰富的路由API,由于之前没有用Node.js做过相关项目,所以就放弃了backbone。Demo通过改变css中display的属性来控制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> Setting</li> 49 <li id="changeUser"><i class="glyphicon glyphicon-transfer"></i> Switch</li> 50 <li id="layout"><i class="glyphicon glyphicon-off"></i> Layout</li> 51 </ul> 52 </div> 53 <ul id="setContent" style="display: none"> 54 <li><i class="glyphicon glyphicon-eye-close"></i><em> Update Password</em></li> 55 <li><i class="glyphicon glyphicon-tags"></i><em> Personal Sign</em></li> 56 <li><i class="glyphicon glyphicon-user"></i><em> 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> 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>
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 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: 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: { 《基于Node.js实现简易聊天室系列之总结》