一、chat目录结构

0913a

图(一)

铛铛前端代码的目录结构如图(一)所示。base文件夹下的.w页面为所有页面的基础页面。Bex5版铛铛和wex5版铛铛的移动端都是从base文件夹中继承的。铛铛Bex5版和wex5版的PC端页面是继承各自的移动端页面。

092011a

在base目录下我们要关注的是base/js文件夹中的im.jsim.impl.js

  • js是dangchat-server所提供的接口,在页面中用到dangchat-server中的方法需要将im.js文件引入。
  • impl.js实现im.js中的接口

对于bex5或wex5有关的接口的实现(例如:铛铛的登录)我们放在各自文件夹中的js目录下,如图(二)所示。

092014a

图(二)

092015a

图(三)

如图(三)所示,lib/actor目录是跟dangchat-server有关的API。调用服务端方法都在actorClient.js中。如图(四)所示:

092028a

图(四)

  1. Promise-pollfill.js是兼容低版本不支持promise的Chrome浏览器。photoswipe是消息页面调用的查看图片的工具。
  2. superInput是输入框有关的组件。比如在PC端移动端会有不同状态、表情、语音都在js中处理。
  3. min.js是PC端复制功能调用的第三方。
  4. electron-app.js是构建桌面端铛铛一些功能。
  5. js是全局页面的一些公共方法。如:会话列表中的最后一条消息的内容、发送人、发送时间时间等。
  6. min.css.xml和chat.min.js.xml是合并资源时所需要的配置文件。
  7. js是配置dangchat-server端口的。
  8. chat根目录下w是兼容以前版本铛铛的首页跳转。默认跳转到bex5版铛铛的移动端首页。

铛铛源码引入步骤及注意点

1.源码准备

用户可在https://github.com/wex5中下载。点击图(五)所示的标题,进入后下载的chat目录为铛铛所有的前端源码。

092016a

图(五)

2.将下载铛铛源码(chat文件夹)的导入x5平台的UI2目录下。

3.Wex5版铛铛源码需要将baas/org文件夹(https://github.com/wex5/wex5-org可下载)导入x5平台下的Baas目录进行模型编译。

注:如果wex5铛铛用bex5数据库中的示例数据。需要注意人员头像问题,wex5版铛铛的头像在数据库中的字段为varchar类型。Bex5版铛铛中示例组织人员的头像为longblob类型。

4. Bex5版铛铛的搜索文档,搜索历史消息功能为x5的拓展服务,点击图(六)所示标题,进入页面进行下载。下载的文件名修改为chat,放入BIZ目录下。如图(七)所示。

092017a

图(六)

092020a

图(七)

5. dangchat-server准备。下载铛铛绿色安装包:http://www.wex5.com/downloads/。如图(八)所示,红框内为dangchat-server的重要文件,可单独拷贝到服务器作为dangchat-server。注:需要修改dangchat-server/conf/server.conf文件中的所在服务器的端口,修改chat/config.js的端口修改为dangchat-server的端口。

092021a

图(八)

  1. 以上步骤完成后,启动dangchat服务,启动tomcat,运行chat/bex5/index.w或chat/wex5/index.w即可。

二chat修改代码重点

1登录逻辑

如果开发者只想用铛铛的服务实现自己登录逻辑。我们需要自己写自己业务层的实现。

以wex5版铛铛为例(UI2\chat\wex5\js\im.wex5.impl.js):登录代码

//param:登录参数
	doLogin : function(param) {
		returnloginByWex5(param);
},
	var loginByWex5 = function(params) {
		varret;
		justep.Baas.sendRequest({
				"url" : "/org/login",
				"action" : "loginAction",
				"async" : false,
				"params" : {
					"userName" : params.username,
					"password" : params.password
				},
					"success" : function(data) {
                        //返回参数为flag(登录是是否成功),
                           //  personID(登录人id),
                           //  personName(登录人name),
                           //  message(登录失败消息)。
                           //  isInOrg(是否在组织中)
						ret = data;			
			});
			returnret;
	};

注:返回personID和personName是为了用这两个字段在dangchat-server中进行注册。

loadPerson为加载组织人员。

加载组织人员是将组织人员构造为person对象,组织人员通过person对象在dangchat-server中进行交互(如:组织人员在dangchat-server注册,获取uid等);

loadPerson : function(persons, pid) {
			var deferred = $.Deferred();
			this.getOrgPersons(pid).done(function(orgPersons) {
                 //将组织人员信息写到新的person对象中
				$.each(orgPersons, function(i, v) {
					var p = {
						id : v.sPersonID,
						name : v.sName,
						uid : v.sNumb,//数据库存放uid的字段
						avatar : getPersonAvatar(v.sPhoto),
						nick : '',
						phones : [],
						about : ''
					};
					persons[v.sPersonID] = new Person(p);
				});
				deferred.resolve(persons);
			});
			return deferred.promise();
		},

2主页面的修改

登录逻辑只是实现当前的人在dangchat-server的中的注册,这时当前人还没在dangchat-server中登录。如果开发者想要基于dangchat-server的API对主页面进行修改,我们必须要等当前人在dangchat-server登录之后,当前人才能调用dangchat-server提供的方法。

例如:会话列表页面(UI2\chat\wex5\main.js)

//登录dangchat-server的方法
this.loginActorDfd = IM.loginActor();
this.loginActorDfd.done(function() {
     //登录dangchat-server之后我们调用API获取会话列表 
IM.bindDialogs(function(dialogs) {

      })
})

注:修改主页面结构时应该考虑页面是否依赖登录dangchat-server。

Wex5工作页面(UI2\chat\wex5\js\im.wex5.impl.js):

在登录时我们将工作页面的需要的登录信息存到了store中

var loginByWex5 = function(params) {
		var ret;
		var userParams = {};
		userParams.CurrentPersonID = "";
		userParams.CurrentPersonName = "";
		userParams.CurrentPersonFID = "";
		userParams.CurrentPersonFName = "";
		userParams.CurrentOgnID = "";
		userParams.CurrentOgnName = "";
		userParams.CurrentDeptID = "";
		userParams.CurrentDeptName = "";
		userParams.CurrentDeptFID = "";
		userParams.CurrentDeptFName = "";
		userParams.CurrentOgnFID = "";
		userParams.CurrentOgnFName = "";
		userParams.CurrentFunRole = "3";// 默认为3:普通员工。(1:公司领导;2:部门主管;3:普通员工)
		justep.Baas.sendRequest({
			"url" : "/org/login",
			"action" : "loginAction",
			"async" : false, 
			"params" : {
				"userName" : params.username,
				"password" : params.password
			},
			"success" : function(data) {
				ret = data;
				if (ret.flag) {
					userParams.CurrentPersonID = ret.personID;
					userParams.CurrentPersonName = ret.personName;
					userParams.CurrentPersonFID = ret.CurrentFID;
					userParams.CurrentPersonFName = ret.CurrentFName;
					userParams.CurrentOgnID = ret.CurrentOgnID;
					userParams.CurrentOgnName = ret.CurrentOgnName;
					userParams.CurrentFunRole = ret.CurrentFunRole;
			userParams.CurrentOgnFID="/"+ret.CurrentFID.split("/")[1];
	userParams.CurrentOgnFName = "/" + ret.CurrentFName.split("/")[1];
					if (ret.CurrentFID.indexOf("dpt") > 0) {
						userParams.CurrentDeptID = ret.CurrentDeptID;
						userParams.CurrentDeptName = ret.CurrentDeptName;
						userParams.CurrentDeptFID = ret.CurrentFID.substring(0, ret.CurrentFID.indexOf("dpt") + 3);
						userParams.CurrentDeptFName = ret.CurrentFName.substring(0, ret.CurrentFName.indexOf("/", ret.CurrentFName.indexOf("/", 2) + 1));
					}
					saveLoginDataToStore(userParams);
				}
			}
		});
		return ret;
	};
	var saveLoginDataToStore = function(data) {
		data = JSON.stringify(data);
		store.set('work_loginData', encrypt(data, password));
	};

工作页面用到当前人的信息只需调用getCurrentInfo()方法。

注:关于发送消息的方法还需要登录dangchat-server。

三、页面结构

如图(九)所示为所有页面的基础页面。bex5铛铛和wex5铛铛调用有关与dangchat-server有关的公共API都在base文件下各自页面的js文件中,bex5铛铛和wex5铛铛继承base文件下页面,调用各自的不同实现。如:doLogin()、loadPerson()、updatePersonUid()。Im.impl.js中的定义的空方法,都是需要bex5的biz或wex5的baas提供相应的方法提供实现。

092022a

如图(十)所示,img文件夹放的是界面用到的一些图片。Js文件中im.js为抽离dangchat-server提供API的接口,im.impl.js是实现im.js中的接口。

图(九)   

      092023a

图(十)

四、页面重点

1会话页面(移动端:main.w PC端:dialogList.w)

main.w为移动端的会话页面;dialogList.w为PC端的会话页面。会话页面中我们调用dangchat-server中的bindDialogs()为返回当前人所有的会话数据。如图(十一)所示。

092024a

图(十一)

counter为未读消息数,date为最后一条消息的发送时间,标注的peer为会话的信息,进入发送消息页面时我们需要此对象。人员会话的peer.id就是我们前文提到的人员的uid。我们可以通过此id调用dangchat-server中的getUserPeer(id)或getGroupPeer(id)获取组的信息和组织人员在dangchat-server中的人员信息。Sender为最后一条消息的发送人。text为最后一条信息的内容。

删除会话功能:调用deleteChat(peer);注:只是人员或群组的删除。不会删除组织机构中人员。

群组的删除并退出:dangchat-server没有提供相应的API,我们调用leaveGroup(peer.id)和deleteChat(peer)实现群组的删除并退出。注:如果只是调用leaveGroup(peer.id)退出可以但是会话列表还会留着,造成多余的空会话列表。

 

注:每当有新的会话时,dangchat-server都会将全部的会列表推送给用户,当会话列表大于二十条时,用户退出重新登录时dangchat-server只会推送会话列表的前二十条。调用onDialogsEnd()查看历史会话。

2消息页面(message.w)

在消息页面我需要每个会话的peer对象。打开消息页面时调用onConversationOpen(peer)可以将会话列表中的counter设置为零。表明你已读未读消息。关闭页面时调用onConversationClosed(peer)。

通过bindMessages(peer,callback)方法在回调函数中取到与该联系人或该群组的聊天内容,每次新打开会话时dangchat-server只会推二十条聊天内容,调用onchatEnd(peer)查看历史消息,调用一次推二十条历史会话,与会话列表相似。消息推送过来后我们调用messageParse对消息进行预处理,分别对表情、文本、文件、图片、语音、和视频分别作了处理,到页面显示相应的页面样式。注:因为消息推送会推送多次,如果消息是文件,消息的推送第一次可能不会将文件的路径推送过来,所以做文件下载时需注意文件的路径。

sendTextMessage(peer,text)是发送文本API,sendFileMessage(peer,file)是发送文件API,在发送文件或文本时dangchat-server会推送新消息的pending(消息追加) sent (消息发送成功) recevied(对方接收) read(接收已读) error(发送失败) 四种状态。

消息页面x5平台为铛铛作了一个输入框组件,为适应不同端输入框的状态。在chat/lib目录下。输入框组件包含css(输入框样式) icon(输入框用到的icon) img(表情图片) js(表情API) lib(第三方录音插件) superInput.js(输入框的核心代码语音录制,手机端键盘高度的计算)。目录结构如图(十二)所示。

092025a

图(十二)

superInput.js中发送发消息,语音发送,截屏等方法。

//发送消息事件
this.$domNode.find('.x-btn-send').on('click',this._doSend.bind(this);
//语音录制及发送事件
this.$domNode.find('.x-btn-voice').on('touchstart mousedown', this.startRecVoice.bind(this)).on('touchend mouseup', this.stopRecVoice.bind(this));
//截屏截屏发送(铛铛pc客户端功能)
this.$domNode.find('.x-btn-prScrn').on('click',this._prScrn.bind(this));

clearChat(peer):删除与该会话的全部消息。

deleteMessage(peer, rid):

删除单条消息(rid为dangchat-server中的消息的id);

关注和取消关注是dangchat-server与bex5结合的一个功能。调用addLike(peer,rid,option);option传递参数如图(十三)所示:

092026a

图(十三)

这些参数是我们调用biz端所需参数。取消关注同理。这样我们就可以查看我们所关注的消息。

3人员列表页面(contactList.w)

此页面为组织机构中的人员信息,bex5铛铛与wex5铛铛实现有差异;bex5铛铛调用biz的方法获取当前人登录的所有组织人员和所有部门。Wex5铛铛调用baas服务获取获取当前人所在组织的所有组织人员和所有部门。bindGroupDialogs(callback)回调函数中获取当前人的所有群组会话,所有群组会话与组织人员构成通讯录页面。此页面差异比较大,不作为基础页面。存放在bex5和wex5目录下。

 

点击群组时传递群组的peer.id和type跳转到群详细页面。

点击组织成员时传递组织人员的id,在contact.w进行处理。对组织人员注册和加好友等操作。此页面为重用页面,点击消息页面消息的转发,wex5的创建群组用此页面进行选择人员操作。

4人员详细页面(contact.w)

在进入页面时通过其他页面传过来的组织人员的id。getPerson(id)获取组织人员的person对象。通过person对象调用addPerson(person),让当前登录人帮助组织人员在dangchat-server中注册并添加此组织人员为好友。调用bindUser(uid)获取该组织人员在dangchat-server中的信息。离开页面时对人员页面进行unbind。

5群组详细页面(group.w)

092027a

图(十四)

进入群组页面我们需要群组的peer.id。bindGroup(peer.id,callback)方法的回调函数中获取群组的详细信息。如图(十四)所示,群组的详细信息页面包含群组的peer.id、群关于(about)、群主的peer.id,群成员(members)、群名称。

6拓展页面

Bex5版铛铛还拓展了一些功能。比如搜索历史消息(findAllText.w),搜索历史文档(searchDocument.w),查看关注页面(msgLike)等。

搜索历史消息API为getSearchText(val, option),具体实现在im.biz.impl.js文件中。

Model.prototype.modelParamsReceive = function(event) {
		this.sId = this.params.sId;
		this.destId = this.params.destId;
		this.groupId = this.params.groupId;
		$(this.getElementByXid("input")).focus();
	};
	Model.prototype.searchClick = function(event) {
		var self = this;
		var rows = [];
		var message;
		var val = $(this.getElementByXid("input")).val();
		var option = {
			sId : this.sId,
			destId : this.destId,
			groupId : this.groupId,
		};
		IM.getSearchText(val, option).done(function(searchText){
			if (searchText.length > 0) {
				for(var i = 0; i < searchText.length; i++){		
					message = searchText[i];
					rows.push({
						fID : message.rid,
						fContent : message.content,
						fMessageTime : message.date.substr(0, message.date.length - 6),
						fImg : IM.getPersonByUID(message.senderUserId).avatar,
						fSenderName : message.senderUserName,
						fDestUserName : message.receiverUserName
					});
				}
				self.comp("textData").loadData(rows);
				$(self.comp("searchText").domNode).show();
			} else {
				justep.Util.hint("暂无此条消息记录");
				self.comp("textData").loadData(rows);
				$(self.comp("searchText").domNode).hide();
			}
		});
	};

在群组搜索聊天信息需要传递搜索关键字,option.sId为当前登录人的peer.id,option.destId可以传null也可传0.option.groupId为群组的peer.id.

与人的聊天会话中将option.destId设置为接收人的peer.id。option.groupId设置为null或0;

搜索文档页面与搜索聊天信息页面相似。只是没有搜索关键字。

关注页面是我们查看前文消息页面关注的那个信息。进入页面是调用getLikeMsgs(filter,limit,offset)获得消息页面关注的信息。页面展示时bind-html即可,关注的那个信息也都带着html标签。