备注:
- 该项目为纯前段静态页面,但是为后台服务的对接提供了良好的接口;
- 部分功能之提供接口,不提供实现;
2.1. 右上角菜单的用户信息,扫一扫按钮(如图4标4);
2.2. 聊天界面的其他功能显示框(如图7标7);
2.3. 通讯录界面的操作按钮(如图5标3);
2.4. 消息列"界面的操作*按钮中的置顶按钮(如图5标2); - 在不同的设备可以自适应,在Iphone 6Plus和IPad mini2经测良好;
欢迎大家拍砖:diary_zhk.sina.com
一. 项目简介
该项目使用angularjs+bootstrip+grunt开发,旨在做一个聊天的前端界面(不包括服务器),分为三个部分,分别是顶部菜单,中间内容,底部导航。实现了消息,通讯录和聊天窗口三个模块。
预览如下:
二. 功能说明
在这里将分为顶部菜单,底部导航,通讯录,消息列表,聊天界面五个部分,分别介绍WeTalk的功能。
1. 顶部菜单
顶部菜单构造如图4所示。
分为四个部分:
- 返回按钮(图4标1) : 该菜单返回消息列表,只有在聊天界面才会显示,会显示未读消息的总数量;
- 标题(图4标2): 显示标题;
- 操作菜单(图4标3,4):提供的操作按钮;
2. 底部导航
底部导航有两个按钮,“消息”和“通讯录”,这两个菜单高度固定,钉在底部。其中“消息”菜单会显示未读消息的总数量。
3. 通讯录
通讯录,顾名思义就是所用可以联系用户的汇总,如图5所示。
分为四个部分:
- 搜索框(图5标1): 在这里,输入联系人的姓名或者姓名的中的某个字,会进行检索,筛选出符合条件的用户;
- 字母检索(图5标2): 按住不放手,在这些字母上滑动,将按照首字母检索,把符合条件的首个用户显示在顶部;
- 操作(图5标3):在某个用户上,向右滑动,将显示出操作按钮;
- 用户条目(图5标4):显示用户信息,包括图片和姓名,点击姓名将进入聊天界面;
4. 消息
消息列表记录最近聊得人,如图6所示。
分为四个部分:
- 搜索框(图6标1):输入内容,自动检索用户名+最后消息内容,显示出符合条件的用户;
- 操作框(图6标2):在某个用户上,向右滑动,将显示出操作按钮;
- 用户信息(图6标3,4):显示用户头像和用户名;
- 最后信息(图6标5,6,7):5表示最后联系内容,6表示该用户未读的消息数量,7表示最后联系时间
5. 聊天界面
聊天界面最主要的功能就是发送和接收消息,暂时支持语音,文字,表情三种内容,如图7]所示。
分为两个部分:
- 消息(图7标1,2,3):1表示用户头像,2表示消息内容,3表示发送或接收消息的时间;
- 消息输入(图7标4,5,6,7,8):4表示文字和语音消息的切换按钮,5消息或者问题输入框,6表示表情显示框,7其他功能显示框,8表示6和7点击之后显示的操作内容;
三. 开发说明
该项目用到的技术或使用的工具有angularjs,bootstrip,grunt,Less,JSHint等。
1. 开发说明
实现了以下功能:
- 多个js,css文件代码合并;
- js,css压缩;
- 根据JSLint对js进行校验;
- 监听文件变更,自动更新;
- 开发环境(default)和正式环境(online)正式校验;
2. 目录结构
在开发过程中,使用模块化的方法组织目录结果,目录结果和各个目录的说明图8.
3. 数据结构
在开发过程中,包括聊天记录,用户信息及其保存都有一定的结构,下面讲述几种主要的数据结构。
3.1 用户信息
如图5中标4表示的一个条目所需要的信息。
{ "id": 1, ----用户编码 "name": "陈奕迅", ----用户姓名 "imgUrl": "/app/displaydata/imgs/chenyixun.png", ----用户头像 "initial":"C" ----首字母,便于查询 }
3.2 消息列表
如图6中标3~7的表示一个条目所需要的信息。
{ userId : 2, ---用户ID userName : "陈奕迅", ---用户名 userImgUrl : "/app/displaydata/imgs/chenyixun.png", ---用户头像 noReadNum: 2, --- 未读的消息数目 lastMsgContent : "最后一条内容", ---最后一条消息的内容(接收或发送) lastMsgTime : 16711312832 --- 最后一条消息的时间(接收或发送) }
3.3 聊天记录
如图7中标1~3的表示一个条目所需要的信息。
{ "id": "1", ---消息ID "content": "我收到你得消息了。-测试", --- 消息内容(文本消息类型) "time": 1465293529060, --- 消息时间(文本消息类型) "status" : 1, ---消息状态(0-成功,1-失败) "sourceType": 1, ---消息来源(1-接收,2-发送) "msgType": 1, ---消息类型(1-文本,2-语音) "src" : '/app/displaydata/test.mp3', ---音频存放位置 "isRead": false ---是否已读 }
3.4 消息缓存
这里采用json的格式,将最近聊天记录和最近联系人保存到浏览器的本地存储空间中( window.localStorage),说明如下:
消息 | key | 格式 | 说明 |
---|---|---|---|
最近联系人 | [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. 技术要点及实现
下面将为大家介绍该系统中比较关键的技术要点及其实现。
4.1 结构层次
该系统采用仿后台“MVC”结构,使用服务器,控制器,路由器,展示器四层结构。用一种业务逻辑、数据、界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑。
4.1.1 服务器
见图8中的services.js文件,这个里面封装了所有对外的服务接口,具体我们分析下面的代码:
//消息服务 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"的服务对象,其中有两个接口,都使用ajax的方式异步请求服务器数据。封装好之后可以将服务器"weService"传递给控制器,以便直接使用服务器定义好的借口。
4.1.2 控制器
见图8中的app/modules//_controlller.js文件,对不同的模块封装了不同的控制器,当然你也可以把所有的控制器放在同一个文件中,但是这样不利于维护。下面我们分析下面的代码:
app.controller("userListController", function ($rootScope, $scope, $location, $timeout, weService) { $rootScope.title = '通讯录'; $scope.getUserList = function(){ $scope.userList = []; //初始化数据-获取用户列表 weService.getUserList().then(function(res){ //原始数据 $scope.userListOrg = res.data; // 搜索的时候回事是变化 $scope.userList = res.data; //整理右侧的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定义的服务器"weService",调用weService的获"取用户列表"接口,实现业务逻辑。
4.1.2 路由器
见图8中的modules.js文件,其通过路由的方式定义了每个控制器和展示页面的关系,具体代码如下:
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”定义了当url为“/userList”时的控制器和展示器。
4.1.4 展示器
展示器就是纯html页面,同时可以将控制层的数据按照指定的方式展示,其中支持EL表达式,{{}},angularjs指令等等,具体情况请参考<a href="http://www.apjs.net/">angularjs教学</a>
4.2 表情
表情有两种表现形式:一种将表情编码,像IOS中的EMOJI表情;另一种就是动态或者静态的图片。这里我们采用第二种,使用动态图片,图片的存放在imgs/face目录下,通过读取文件名,对表情进行简单的编码,编码规则为:
[em-文件名]
以编码形式进行保存,当需要显示时通过对编码的解析,转换成img标签:
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; };