備注:
- 該項目為純前段靜態(tài)頁面,但是為后臺服務(wù)的對接提供了良好的接口呻待;
- 部分功能之提供接口笛粘,不提供實現(xiàn)趁怔;
2.1. 右上角菜單的用戶信息湿硝,掃一掃按鈕(如圖4標(biāo)4);
2.2. 聊天界面的其他功能顯示框(如圖7標(biāo)7)润努;
2.3. 通訊錄界面的操作按鈕(如圖5標(biāo)3)关斜;
2.4. 消息列"界面的操作*按鈕中的置頂按鈕(如圖5標(biāo)2); - 在不同的設(shè)備可以自適應(yīng)铺浇,在Iphone 6Plus和IPad mini2經(jīng)測良好痢畜;
歡迎大家拍磚:diary_zhk.sina.com
一. 項目簡介
該項目使用angularjs+bootstrip+grunt開發(fā),旨在做一個聊天的前端界面(不包括服務(wù)器)鳍侣,分為三個部分丁稀,分別是頂部菜單,中間內(nèi)容倚聚,底部導(dǎo)航线衫。實現(xiàn)了消息,通訊錄和聊天窗口三個模塊惑折。
預(yù)覽如下:
二. 功能說明
在這里將分為頂部菜單授账,底部導(dǎo)航,通訊錄惨驶,消息列表白热,聊天界面五個部分,分別介紹WeTalk的功能敞咧。
1. 頂部菜單
頂部菜單構(gòu)造如圖4所示棘捣。
分為四個部分:
- 返回按鈕(圖4標(biāo)1) : 該菜單返回消息列表,只有在聊天界面才會顯示休建,會顯示未讀消息的總數(shù)量乍恐;
- 標(biāo)題(圖4標(biāo)2): 顯示標(biāo)題;
- 操作菜單(圖4標(biāo)3,4):提供的操作按鈕测砂;
2. 底部導(dǎo)航
底部導(dǎo)航有兩個按鈕茵烈,“消息”和“通訊錄”,這兩個菜單高度固定砌些,釘在底部呜投。其中“消息”菜單會顯示未讀消息的總數(shù)量。
3. 通訊錄
通訊錄存璃,顧名思義就是所用可以聯(lián)系用戶的匯總仑荐,如圖5所示。
分為四個部分:
- 搜索框(圖5標(biāo)1): 在這里纵东,輸入聯(lián)系人的姓名或者姓名的中的某個字粘招,會進(jìn)行檢索,篩選出符合條件的用戶偎球;
- 字母檢索(圖5標(biāo)2): 按住不放手洒扎,在這些字母上滑動辑甜,將按照首字母檢索,把符合條件的首個用戶顯示在頂部袍冷;
- 操作(圖5標(biāo)3):在某個用戶上磷醋,向右滑動,將顯示出操作按鈕胡诗;
- 用戶條目(圖5標(biāo)4):顯示用戶信息邓线,包括圖片和姓名,點擊姓名將進(jìn)入聊天界面煌恢;
4. 消息
消息列表記錄最近聊得人褂痰,如圖6所示。
分為四個部分:
- 搜索框(圖6標(biāo)1):輸入內(nèi)容症虑,自動檢索用戶名+最后消息內(nèi)容缩歪,顯示出符合條件的用戶;
- 操作框(圖6標(biāo)2):在某個用戶上谍憔,向右滑動匪蝙,將顯示出操作按鈕;
- 用戶信息(圖6標(biāo)3,4):顯示用戶頭像和用戶名习贫;
- 最后信息(圖6標(biāo)5,6,7):5表示最后聯(lián)系內(nèi)容逛球,6表示該用戶未讀的消息數(shù)量,7表示最后聯(lián)系時間
5. 聊天界面
聊天界面最主要的功能就是發(fā)送和接收消息苫昌,暫時支持語音颤绕,文字,表情三種內(nèi)容祟身,如圖7]所示奥务。
分為兩個部分:
- 消息(圖7標(biāo)1,2,3):1表示用戶頭像,2表示消息內(nèi)容袜硫,3表示發(fā)送或接收消息的時間氯葬;
- 消息輸入(圖7標(biāo)4,5,6,7,8):4表示文字和語音消息的切換按鈕婉陷,5消息或者問題輸入框帚称,6表示表情顯示框,7其他功能顯示框秽澳,8表示6和7點擊之后顯示的操作內(nèi)容闯睹;
三. 開發(fā)說明
該項目用到的技術(shù)或使用的工具有angularjs,bootstrip担神,grunt民假,Less叛溢,JSHint等。
1. 開發(fā)說明
實現(xiàn)了以下功能:
- 多個js,css文件代碼合并栅受;
- js,css壓縮滩届;
- 根據(jù)JSLint對js進(jìn)行校驗诊沪;
- 監(jiān)聽文件變更,自動更新砌函;
- 開發(fā)環(huán)境(default)和正式環(huán)境(online)正式校驗斩披;
2. 目錄結(jié)構(gòu)
在開發(fā)過程中,使用模塊化的方法組織目錄結(jié)果讹俊,目錄結(jié)果和各個目錄的說明圖8.
3. 數(shù)據(jù)結(jié)構(gòu)
在開發(fā)過程中垦沉,包括聊天記錄,用戶信息及其保存都有一定的結(jié)構(gòu)仍劈,下面講述幾種主要的數(shù)據(jù)結(jié)構(gòu)厕倍。
3.1 用戶信息
如圖5中標(biāo)4表示的一個條目所需要的信息。
{ "id": 1, ----用戶編碼 "name": "陳奕迅", ----用戶姓名 "imgUrl": "/app/displaydata/imgs/chenyixun.png", ----用戶頭像 "initial":"C" ----首字母贩疙,便于查詢 }
3.2 消息列表
如圖6中標(biāo)3~7的表示一個條目所需要的信息讹弯。
{ userId : 2, ---用戶ID userName : "陳奕迅", ---用戶名 userImgUrl : "/app/displaydata/imgs/chenyixun.png", ---用戶頭像 noReadNum: 2, --- 未讀的消息數(shù)目 lastMsgContent : "最后一條內(nèi)容", ---最后一條消息的內(nèi)容(接收或發(fā)送) lastMsgTime : 16711312832 --- 最后一條消息的時間(接收或發(fā)送) }
3.3 聊天記錄
如圖7中標(biāo)1~3的表示一個條目所需要的信息。
{ "id": "1", ---消息ID "content": "我收到你得消息了这溅。-測試", --- 消息內(nèi)容(文本消息類型) "time": 1465293529060, --- 消息時間(文本消息類型) "status" : 1, ---消息狀態(tài)(0-成功组民,1-失敗) "sourceType": 1, ---消息來源(1-接收悲靴,2-發(fā)送) "msgType": 1, ---消息類型(1-文本臭胜,2-語音) "src" : '/app/displaydata/test.mp3', ---音頻存放位置 "isRead": false ---是否已讀 }
3.4 消息緩存
這里采用json的格式,將最近聊天記錄和最近聯(lián)系人保存到瀏覽器的本地存儲空間中( window.localStorage)癞尚,說明如下:
消息 | key | 格式 | 說明 |
---|---|---|---|
最近聯(lián)系人 | [loginUserId]_recent_users | JsonArray | 例如:10000_recent_users: "[{"userId":8,"userName":"希特勒","userImgUrl":"/app/displaydata/imgs/xitele.png"}]"
|
歷史消息 | [loginUserId]_history_[toUserId] | JsonArray | 例如:10000_history_41:"[{"id":1,"content":"測試-收到消息 ","time":1469499329490,"sourceType":1,"msgType":1,"status":0,"isRead":false}]" 耸三, 具體請參見3.3聊天記錄 |
4. 技術(shù)要點及實現(xiàn)
下面將為大家介紹該系統(tǒng)中比較關(guān)鍵的技術(shù)要點及其實現(xiàn)。
4.1 結(jié)構(gòu)層次
該系統(tǒng)采用仿后臺“MVC”結(jié)構(gòu)浇揩,使用服務(wù)器吕晌,控制器,路由器临燃,展示器四層結(jié)構(gòu)睛驳。用一種業(yè)務(wù)邏輯、數(shù)據(jù)膜廊、界面顯示分離的方法組織代碼乏沸,將業(yè)務(wù)邏輯聚集到一個部件里面,在改進(jìn)和個性化定制界面及用戶交互的同時爪瓜,不需要重新編寫業(yè)務(wù)邏輯蹬跃。
4.1.1 服務(wù)器
見圖8中的services.js文件,這個里面封裝了所有對外的服務(wù)接口铆铆,具體我們分析下面的代碼:
//消息服務(wù) app.service("weService", function ($http) { return { /* 獲取用戶列表 */ getUserList : function () { return $http({ url : '/app/displaydata/userlist.json', method : 'get', dataType : 'json' }); }, /*獲取聊天界面的操作列表*/ getMsgToolsList : function () { return $http({ url : '/app/modules/base/data/messageTools.json', method : 'get', dataType : 'json' }); } }; });
這段代碼封裝了一個名為"weService"的服務(wù)對象蝶缀,其中有兩個接口丹喻,都使用ajax的方式異步請求服務(wù)器數(shù)據(jù)。封裝好之后可以將服務(wù)器"weService"傳遞給控制器翁都,以便直接使用服務(wù)器定義好的借口碍论。
4.1.2 控制器
見圖8中的app/modules//_controlller.js文件,對不同的模塊封裝了不同的控制器柄慰,當(dāng)然你也可以把所有的控制器放在同一個文件中鳍悠,但是這樣不利于維護(hù)。下面我們分析下面的代碼:
app.controller("userListController", function ($rootScope, $scope, $location, $timeout, weService) { $rootScope.title = '通訊錄'; $scope.getUserList = function(){ $scope.userList = []; //初始化數(shù)據(jù)-獲取用戶列表 weService.getUserList().then(function(res){ //原始數(shù)據(jù) $scope.userListOrg = res.data; // 搜索的時候回事是變化 $scope.userList = res.data; //整理右側(cè)的query $scope.queryKeyList = []; angular.forEach($scope.userList, function(user){ var queryKey = user.initial; var flag = true; angular.forEach($scope.queryKeyList, function(qk){ if(qk === queryKey){ flag = false; return ; } }); if(flag){ $scope.queryKeyList.push(queryKey); } }); //$timeout(function(){myScroll.refresh();},200);; }); }; $scope.getUserList(); });
上述代碼封裝了一個名為"userListController"的控制器坐搔,在初始化方法中使用4.1.1定義的服務(wù)器"weService"藏研,調(diào)用weService的獲"取用戶列表"接口,實現(xiàn)業(yè)務(wù)邏輯。
4.1.2 路由器
見圖8中的modules.js文件概行,其通過路由的方式定義了每個控制器和展示頁面的關(guān)系蠢挡,具體代碼如下:
app.config( function ($routeProvider) { $routeProvider .when('/userList', { templateUrl: "/app/modules/user/htmls/user_list.html", controller : "userListController" }) .otherwise({ templateUrl: "/app/modules/base/htmls/unknow.part.html", controller : function($rootScope, $scope){ $rootScope.bodyClass = 'error-page'; } }); });
上述代碼定義了兩個路由,一個“/userList”凳忙,一個“otherwise”袒哥。“otherwise”顧名思義就是找不到指定路由時處理方法消略;“/userList”定義了當(dāng)url為“/userList”時的控制器和展示器堡称。
4.1.4 展示器
展示器就是純html頁面,同時可以將控制層的數(shù)據(jù)按照指定的方式展示艺演,其中支持EL表達(dá)式却紧,{{}},angularjs指令等等胎撤,具體情況請參考<a >angularjs教學(xué)</a>
4.2 表情
表情有兩種表現(xiàn)形式:一種將表情編碼晓殊,像IOS中的EMOJI表情;另一種就是動態(tài)或者靜態(tài)的圖片伤提。這里我們采用第二種巫俺,使用動態(tài)圖片,圖片的存放在imgs/face目錄下肿男,通過讀取文件名介汹,對表情進(jìn)行簡單的編碼,編碼規(guī)則為:
[em-文件名]
以編碼形式進(jìn)行保存舶沛,當(dāng)需要顯示時通過對編碼的解析嘹承,轉(zhuǎn)換成img標(biāo)簽:
function(str){ str = str.replace(/</g,'<'); str = str.replace(/>/g,'>'); str = str.replace(/\n/g,'<br/>'); str = str.replace(/\[em-([0-9]*)\]/g, '<img src="/imgs/face/$1.gif" border="0"/>'); return str; };