一、chat目录结构
图(一)
铛铛前端代码的目录结构如图(一)所示。base文件夹下的.w页面为所有页面的基础页面。Bex5版铛铛和wex5版铛铛的移动端都是从base文件夹中继承的。铛铛Bex5版和wex5版的PC端页面是继承各自的移动端页面。
在base目录下我们要关注的是base/js文件夹中的im.js和im.impl.js。
- js是dangchat-server所提供的接口,在页面中用到dangchat-server中的方法需要将im.js文件引入。
- impl.js实现im.js中的接口。
对于bex5或wex5有关的接口的实现(例如:铛铛的登录)我们放在各自文件夹中的js目录下,如图(二)所示。
图(二)
图(三)
如图(三)所示,lib/actor目录是跟dangchat-server有关的API。调用服务端方法都在actorClient.js中。如图(四)所示:
图(四)
- Promise-pollfill.js是兼容低版本不支持promise的Chrome浏览器。photoswipe是消息页面调用的查看图片的工具。
- superInput是输入框有关的组件。比如在PC端移动端会有不同状态、表情、语音都在js中处理。
- min.js是PC端复制功能调用的第三方。
- electron-app.js是构建桌面端铛铛一些功能。
- js是全局页面的一些公共方法。如:会话列表中的最后一条消息的内容、发送人、发送时间时间等。
- min.css.xml和chat.min.js.xml是合并资源时所需要的配置文件。
- js是配置dangchat-server端口的。
- chat根目录下w是兼容以前版本铛铛的首页跳转。默认跳转到bex5版铛铛的移动端首页。
铛铛源码引入步骤及注意点
1.源码准备
用户可在https://github.com/wex5中下载。点击图(五)所示的标题,进入后下载的chat目录为铛铛所有的前端源码。
图(五)
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目录下。如图(七)所示。
图(六)
图(七)
5. dangchat-server准备。下载铛铛绿色安装包:http://www.wex5.com/downloads/。如图(八)所示,红框内为dangchat-server的重要文件,可单独拷贝到服务器作为dangchat-server。注:需要修改dangchat-server/conf/server.conf文件中的所在服务器的端口,修改chat/config.js的端口修改为dangchat-server的端口。
图(八)
- 以上步骤完成后,启动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提供相应的方法提供实现。

如图(十)所示,img文件夹放的是界面用到的一些图片。Js文件中im.js为抽离dangchat-server提供API的接口,im.impl.js是实现im.js中的接口。
图(九)
图(十)
四、页面重点
1会话页面(移动端:main.w PC端:dialogList.w)
main.w为移动端的会话页面;dialogList.w为PC端的会话页面。会话页面中我们调用dangchat-server中的bindDialogs()为返回当前人所有的会话数据。如图(十一)所示。
图(十一)
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(输入框的核心代码语音录制,手机端键盘高度的计算)。目录结构如图(十二)所示。
图(十二)
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传递参数如图(十三)所示:
图(十三)
这些参数是我们调用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)
图(十四)
进入群组页面我们需要群组的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标签。














评一波