Created
December 23, 2016 09:36
-
-
Save TooBug/175428658f0d3e081fc977809c4b9918 to your computer and use it in GitHub Desktop.
未上线的聊天系统前端代码
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 封装Comet长连接操作,其中extraRequestData暴露出来,可供动态修改下次请求时的参数 | |
* @author TooBug | |
* @version 2014-08-02 | |
* @example | |
var a=new Comet({ | |
url:'http://echo.113.im/', | |
data:{ | |
data:JSON.stringify({test:123}), | |
timeout:10000 | |
}, | |
onData:function(data){ | |
console.log(data); | |
if(!this.extraRequestData.test){ | |
this.extraRequestData.test = 1; | |
}else{ | |
this.extraRequestData.test ++; | |
} | |
} | |
}); | |
*/ | |
define([ | |
'jquery' | |
],function($){ | |
// 默认配置项 | |
var defaultOptions = { | |
onData:$.noop, | |
onClose:$.noop, | |
onError:$.noop, | |
dataType:'json', | |
data:{}, | |
method:'get', | |
timeout:300*1000 //默认300秒超时 | |
}; | |
/** | |
* @constructor Comet构造函数 | |
* @param {Object} options 参数对象 | |
* @param {String} options.url 远程地址URL | |
* @param {Object} options.data 请求要带的参数 | |
* @param {String} options.dataType 数据类型,与jQuery.ajax相同 | |
* @param {Number} options.timeout Ajax超时时间(非Comet超时时间,Comet超时由后台定义) | |
* @param {Function} options.onData 有数据返回时的回调函数,this为Comet实例 | |
* @param {Function} options.onError 出错时的回调函数,this为Comet实例 | |
* @param {Function} options.onClose 关闭连接时的回调函数,this为Comet实例 | |
* @return {Object} Comet实例 | |
*/ | |
var Comet = function(options){ | |
this.init(options); | |
}; | |
/** | |
* Comet实例初始化 | |
* @param {Object} options 与默认参数合并后的参数对象,与构建函数接受的参数项目相同 | |
* @return {Object} 返回当前Comet实例 | |
*/ | |
Comet.prototype.init = function(options){ | |
this.options = $.extend({},defaultOptions,options); | |
this.extraRequestData = {}; | |
this.doConnect(); | |
this.working = true; | |
return this; | |
}; | |
/** | |
* 连接远程服务器 | |
* @return {Object} 返回当前Comet实例 | |
*/ | |
Comet.prototype.doConnect = function(){ | |
var _this = this; | |
var data = $.extend({},this.extraRequestData,this.options.data); | |
$.ajax({ | |
url:this.options.url, | |
dataType:this.options.dataType, | |
data:data, | |
type:this.options.method, | |
timeout:this.options.timeout | |
}).done(function(data){ | |
if(_this.working){ | |
_this.options.onData.call(_this,data); | |
} | |
}).fail(function(xhr,msg,err){ | |
if(_this.working){ | |
_this.options.onError.call(_this,msg,err); | |
} | |
}).always(function(){ | |
if(_this.working){ | |
_this.doConnect(); | |
} | |
}); | |
return this; | |
}; | |
/** | |
* 暂停Comet实例工作,将暂停ajax轮询及函数回调(包括已经在进行中的请求的回调) | |
* @return {Object} 返回当前Comet实例 | |
*/ | |
Comet.prototype.pause = function(){ | |
this.working = false; | |
return this; | |
}; | |
/** | |
* 恢复Comet实例工作,将立刻发起连接 | |
* @return {Object} 返回当前Comet实例 | |
*/ | |
Comet.prototype.resume = function(){ | |
this.doConnect(); | |
this.working = true; | |
return this; | |
}; | |
/** | |
* 关闭Comet实例连接 | |
* @return {Object} 返回当前Comet实例 | |
*/ | |
Comet.prototype.close = function(){ | |
this.working = false; | |
this.options.onClose.call(this); | |
return this; | |
}; | |
return Comet; | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*global moment*/ | |
define([ | |
'underscore.min', | |
'jquery', | |
'app/futu5/chat/memberpicker', | |
'app/futu5/chat/emotion' | |
],function(_,$,memberPicker,emotion){ | |
var messageItemTmpl = $('#messageItemTmpl').html(); | |
var renderMessageFunc = _.template(messageItemTmpl); | |
var $chatBox = $('#chatBox'); | |
var $conversationDetail = $('#conversationDetail'); | |
var $messageWrapper = $('#messageWrapper'); | |
var $messageTitle = $('#messageTitle'); | |
var $showDetail = $('#operateWrapper .showDetail'); | |
var $isBlockedTrue = $('#operateWrapper .isBlockedTrue'); | |
var $isBlockedFalse = $('#operateWrapper .isBlockedFalse'); | |
var $doBlockConversation = $('#operateWrapper .doBlockConversation'); | |
var $doUnblockConversation = $('#operateWrapper .doUnblockConversation'); | |
var conversationMemberItemTmpl = $('#conversationMemberItemTmpl').html(); | |
var renderConversationMemberFunc = _.template(conversationMemberItemTmpl); | |
var currentConversation; | |
var Conversation = function(conversation){ | |
$.extend(this,conversation); | |
this.messageList = this.messageList || []; | |
// this.active(); | |
}; | |
Conversation.TYPE_MAP = { | |
1:'group', | |
2:'single', | |
3:'stock', | |
4:'service', | |
5:'bbs', | |
6:'notice', | |
7:'company' | |
}; | |
Conversation.ROLE_MAP = { | |
1:'master', | |
3:'member' | |
}; | |
Conversation.STATUS_MAP = { | |
1:'normal' | |
}; | |
Conversation.MESSAGE_TYPE_MAP = { | |
1:'text', | |
2:'image', | |
3:'invited', | |
4:'exited', | |
5:'kicked', | |
6:'titleChangeed' | |
}; | |
/*Conversation.getCurrnetConversation = function(){ | |
return currentConversation; | |
};*/ | |
Conversation.scroll2Bottom = function(){ | |
$messageWrapper.scrollTop($messageWrapper.get(0).scrollHeight); | |
}; | |
Conversation.clearMessageList = function(){ | |
$messageWrapper.empty(); | |
}; | |
Conversation.prototype.dealFeeds = function(message){ | |
if(message.userId === window.MyUid) return; | |
var _this = this; | |
var uid; | |
if(message.type === 'invited'){ | |
for(uid in message.extra.user_list){ | |
this.memberList.push({ | |
uid:+uid, | |
nickname:message.extra.user_list[uid].member_nick, | |
avatarUrl:message.extra.user_list[uid].member_icon, | |
}); | |
} | |
}else if(message.type === 'exited'){ | |
this.memberList.forEach(function(memberItem,index){ | |
if(memberItem.uid === message.userId){ | |
_this.memberList.splice(index,1); | |
} | |
}); | |
}else if(message.type === 'kicked'){ | |
for(uid in message.extra.user_list){ | |
this.memberList.forEach(function(memberItem,index){ | |
if(memberItem.uid === +uid){ | |
_this.memberList.splice(index,1); | |
} | |
// 如果自己被T | |
if(+uid === window.MyUid){ | |
alert('您已被群主 '+message.nickname+' 移出讨论组 “'+currentConversation.title+'”'); | |
$(window).trigger('Conversation_Quit',[currentConversation.conversationId]); | |
} | |
}); | |
} | |
}else if(message.type === 'titleChangeed'){ | |
this.title = message.extra.group_name; | |
} | |
this.renderConversationDetail(); | |
$(window).trigger('Conversation_Update',[this.conversationId]); | |
}; | |
Conversation.prototype.renderMessage = function(data,onlyReturn){ | |
var renderMap = { | |
text:{ | |
type:'text', | |
}, | |
image:{ | |
type:'image', | |
}, | |
invited:{ | |
type:'notice', | |
html:'{%=nickname%}邀请 {%for(uid in extra.user_list){%}'+ | |
'{%=extra.user_list[uid].member_nick%} '+ | |
'{%}%}加入了会话' | |
}, | |
exited:{ | |
type:'notice', | |
html:'{%=nickname%}退出了会话' | |
}, | |
kicked:{ | |
type:'notice', | |
html:'{%=nickname%}将 {%for(uid in extra.user_list){%}'+ | |
'{%=extra.user_list[uid].member_nick%} '+ | |
'{%}%}踢出了会话' | |
}, | |
titleChangeed:{ | |
type:'notice', | |
html:'{%=nickname%}将会话标题修改为“{%=extra.group_name%}”' | |
}, | |
}; | |
var render = renderMap[data.type || 'text']; | |
var html; | |
switch(render.type){ | |
case 'text': | |
html = renderMessageFunc(data); | |
break; | |
case 'notice': | |
html = '<div class="box_notice">'+_.template(render.html)(data)+'</div>'; | |
break; | |
} | |
if(onlyReturn){ | |
return html; | |
} | |
$messageWrapper.append(html); | |
Conversation.scroll2Bottom(); | |
}; | |
Conversation.prototype.renderAllMessage = function(){ | |
var _this = this; | |
var html = this.messageList.map(function(messageItem){ | |
return _this.renderMessage(messageItem,true); | |
}); | |
$messageWrapper.html(html); | |
}; | |
Conversation.prototype.renderTitle = function(){ | |
$messageTitle.text(this.title); | |
}; | |
Conversation.prototype.renderIsBlocked = function(){ | |
if(this.isBlocked){ | |
$isBlockedTrue.show(); | |
$isBlockedFalse.hide(); | |
}else{ | |
$isBlockedTrue.hide(); | |
$isBlockedFalse.show(); | |
} | |
}; | |
Conversation.prototype.renderConversationDetail = function(){ | |
$conversationDetail.find('.memberCount span').text(this.memberList.length); | |
$conversationDetail.find('.conversationName').val(this.title); | |
$messageTitle.text(this.title); | |
$conversationDetail.find('.conversationMemberList').find('.conversationMemberItem').remove().end() | |
.prepend(renderConversationMemberFunc(this.memberList)); | |
}; | |
Conversation.prototype.pushMessage = function(message){ | |
this.messageList.push(message); | |
if(!this.isActive){ | |
if(message.type === 'text' || message.type === 'image'){ | |
this.unreadCount++; | |
} | |
return; | |
} | |
if((message.type !== 'text' && message.type !== 'image') || | |
message.role === 'other' || !Conversation.me){ | |
this.renderMessage(message); | |
} | |
}; | |
Conversation.prototype.getHistory = function(timestamp,direction){ | |
return $.getJSON('/chat/get-group-message',{group_id:this.conversationId}).then(ajaxDoneFilter,ajaxFailFilter); | |
}; | |
Conversation.prototype.update = function(){ | |
var _this = this; | |
$.getJSON('/chat/group_info',{ | |
group_id:this.conversationId | |
}).then(ajaxDoneFilter,ajaxFailFilter).done(function(data){ | |
_this.avatarUrl = data.group_info.group_image_url; | |
_this.title = data.group_info.group_name; | |
_this.type = Conversation.TYPE_MAP[data.group_info.type]; | |
_this.isBlocked = +data.is_shielded; | |
delete _this._isFake; | |
_this.memberList = data.group_info.member_list.map(function(memberItem){ | |
return { | |
uid:+memberItem.member_uid, | |
role:Conversation.ROLE_MAP[memberItem.role], | |
status:Conversation.STATUS_MAP[memberItem.status], | |
avatarUrl:memberItem.member_icon, | |
nickname:memberItem.member_nick, | |
isBlocked:+memberItem.is_shielded | |
}; | |
}); | |
_this.renderTitle(); | |
_this.renderIsBlocked(); | |
_this.renderConversationDetail(); | |
$(window).trigger('Conversation_Update',[_this.conversationId]); | |
}); | |
}; | |
Conversation.prototype.updateUnreadCount = function(){ | |
this.unreadCount = 0; | |
}; | |
Conversation.prototype.active = function(){ | |
currentConversation = this; | |
var _this = this; | |
Conversation.clearMessageList(); | |
this.renderTitle(); | |
this.renderIsBlocked(); | |
this.renderConversationDetail(); | |
this.isActive = true; | |
this.switchDetail(false); | |
if(this.messageList && this.messageList.length){ | |
doRender(); | |
}else{ | |
this.getHistory().done(function(data){ | |
if(!data.message_list){ | |
_this.messageList = []; | |
}else{ | |
_this.messageList = data.message_list.map(function(item){ | |
return { | |
messageId:+item.msg_id, | |
avatarUrl:item.member_icon, | |
nickname:item.member_nick, | |
role:+item.member_uid === window.MyUid?'my':'other', | |
userId:+item.member_uid, | |
time:item.create_time, | |
content:item.content, | |
type:Conversation.MESSAGE_TYPE_MAP[item.msg_type], | |
extra:item.extra | |
}; | |
}); | |
} | |
doRender(); | |
}); | |
} | |
function doRender(){ | |
if(!_this.isActive) return; | |
_this.messageList.sort(function(message1,message2){ | |
return message1.time < message2.time?-1:1; | |
}); | |
_this.renderAllMessage(); | |
Conversation.scroll2Bottom(); | |
_this.updateUnreadCount(); | |
updateReadTime(_this.conversationId); | |
// 发出事件,聚焦了,由list处理高亮 | |
$(window).trigger('Conversation_Active',[currentConversation.conversationId]); | |
} | |
}; | |
Conversation.prototype.sendMessage = function(content){ | |
var _this = this; | |
var dfd = $.Deferred(); | |
// 为了体验,先渲染,再在后台发送 | |
var message = { | |
userId:window.MyUid, | |
time:Date.now(), | |
content:htmlEncode(content), | |
role:'my' | |
}; | |
if(Conversation.me){ | |
message.avatarUrl = Conversation.me.avatarUrl; | |
message.nickname = Conversation.me.nickname; | |
_this.renderMessage(message); | |
} | |
$.post('/chat/send-message',{ | |
content:content, | |
group_id:this.conversationId | |
},$.noop,'json').then(ajaxDoneFilter,ajaxFailFilter).done(function(data){ | |
dfd.resolve(); | |
}).fail(function(){ | |
_this.renderMessage({ | |
avatarUrl:'https://my.futu5.com/images/noimg/bbsHead.jpg?2014.07.28.5.hk', | |
nickname:'test', | |
role:'my', | |
userId:1234, | |
time:Date.now(), | |
content:'“'+content+'”发送失败……' | |
}); | |
dfd.reject(); | |
}); | |
return dfd; | |
}; | |
Conversation.prototype.switchDetail = function(isShow){ | |
if(isShow){ | |
// if(this.type !== 'group') return; | |
$conversationDetail.show(); | |
$chatBox.hide(); | |
}else{ | |
$conversationDetail.hide(); | |
$chatBox.show(); | |
} | |
}; | |
Conversation.prototype.switchBlocked = function(isBlocked){ | |
var _this = this; | |
$.post('/chat/shield-group',{ | |
group_id:this.conversationId, | |
type:isBlocked?undefined:1 | |
},$.noop,'json').then(ajaxDoneFilter,ajaxFailFilter).done(function(){ | |
_this.isBlocked = isBlocked; | |
_this.renderIsBlocked(); | |
}).fail(function(){ | |
}); | |
}; | |
Conversation.prototype.delete = function(){ | |
var _this = this; | |
$.post('/chat/delete-group',{ | |
group_id:this.conversationId | |
},$.noop,'json').then(ajaxDoneFilter,ajaxFailFilter).done(function(){ | |
// 发出删除事件,列表接收事件处理会话列表 | |
$(window).trigger('Conversation_Delete',[_this.conversationId]); | |
}).fail(function(msg){ | |
alert(msg); | |
}); | |
}; | |
Conversation.bindEvents = function(){ | |
var _this = this; | |
// 发送消息 | |
$('#sendMessage').click(function(){ | |
var $textarea = $('#sendWrapper textarea'); | |
if(!$textarea.val()) return false; | |
if(!currentConversation){ | |
alert('暂无会话,请先新建会话再发送消息!'); | |
return false; | |
} | |
currentConversation.sendMessage($textarea.val()).done(function(){ | |
}).fail(function(){ | |
$textarea.val($textarea.data('lastval')); | |
}); | |
// 为了体验,先清空再发送 | |
$textarea.data('lastval',$textarea.val()).val(''); | |
return false; | |
}); | |
// CTRL/Command+ENTER | |
$('#sendWrapper textarea').on('keydown',function(e){ | |
var ctrlOrCmd = e.metaKey || e.ctrlKey; | |
if(e.keyCode === 13 && ctrlOrCmd){ | |
$('#sendMessage').click(); | |
return false; | |
} | |
}); | |
// 表情 | |
$('#sendWrapper .insertEmotion').on('click',function(){ | |
emotion({ | |
$trigger:$(this), | |
$container:$(this), | |
type:'niuniu', | |
$input:$('#sendWrapper textarea') | |
}); | |
return false; | |
}); | |
// 是否屏蔽 | |
$doUnblockConversation.click(function(){ | |
currentConversation.switchBlocked(false); | |
}); | |
$doBlockConversation.click(function(){ | |
currentConversation.switchBlocked(true); | |
}); | |
// 显示会话详情 | |
$showDetail.click(function(){ | |
currentConversation.switchDetail(true); | |
return false; | |
}); | |
// 关闭会话详情 | |
$conversationDetail.find('.closeBtn').click(function(){ | |
currentConversation.switchDetail(false); | |
return false; | |
}); | |
// 添加成员 | |
$conversationDetail.find('.conversationAddMember').click(function(){ | |
memberPicker.init({ | |
onSelect:function(memberList){ | |
var memberStr = memberList.map(function(memberItem){ | |
return memberItem.uid; | |
}).join(','); | |
$.post('/chat/add-member',{ | |
group_id:currentConversation.conversationId, | |
uid_str:memberStr | |
},$.noop,'json').then(ajaxDoneFilter,ajaxFailFilter).done(function(data){ | |
memberList.forEach(function(newMemberItem){ | |
if(!currentConversation.memberList.some(function(exsitMemberItem){ | |
return exsitMemberItem.uid === newMemberItem.uid; | |
})){ | |
currentConversation.memberList.push({ | |
uid:newMemberItem.uid, | |
role:'member', | |
status:'normal', | |
avatarUrl:newMemberItem.avatarUrl, | |
nickname:newMemberItem.nickname, | |
isBlocked:false | |
}); | |
} | |
}); | |
currentConversation.renderConversationDetail(); | |
}).fail(function(msg){ | |
alert('添加成员失败:'+msg); | |
}); | |
memberPicker.hide(); | |
} | |
}); | |
return false; | |
}); | |
// 删除成员 | |
$conversationDetail.on('click','.deleteMember',function(){ | |
var uid = +$(this).closest('li').data('uid'); | |
$.post('/chat/delete-member',{ | |
group_id:currentConversation.conversationId, | |
uid_str:uid | |
},$.noop,'json').then(ajaxDoneFilter,ajaxFailFilter).done(function(data){ | |
currentConversation.memberList.forEach(function(memberItem,index){ | |
if(memberItem.uid === uid){ | |
currentConversation.memberList.splice(index,1); | |
} | |
}); | |
currentConversation.renderConversationDetail(); | |
}).fail(function(msg){ | |
alert('删除成员失败:'+msg); | |
}); | |
}); | |
// 更改群聊名称 | |
$conversationDetail.find('.conversationName').on('blur',function(){ | |
var $input = this; | |
var newTitle = $input.value; | |
if(newTitle === currentConversation.title) return; | |
if(newTitle){ | |
$.post('/chat/modify-group-name',{ | |
group_id:currentConversation.conversationId, | |
group_name:newTitle | |
},$.noop,'json').then(ajaxDoneFilter,ajaxFailFilter).done(function(data){ | |
currentConversation.title = newTitle; | |
currentConversation.renderTitle(); | |
}).fail(function(msg){ | |
alert(msg); | |
$input.value = currentConversation.title; | |
}); | |
} | |
}); | |
// 退出聊天 | |
$('#operateWrapper .quitBtn').click(function(){ | |
$.post('/chat/quit-group',{ | |
group_id:currentConversation.conversationId | |
},$.noop,'json').then(ajaxDoneFilter,ajaxFailFilter).done(function(data){ | |
// 发出退出会话的事件,让list来处理 | |
$(window).trigger('Conversation_Quit',[currentConversation.conversationId]); | |
}).fail(function(msg){ | |
alert(msg); | |
}); | |
return false; | |
}); | |
}; | |
Conversation.createConversation = function(type,members){ | |
var dfd = $.Deferred(); | |
$.post('/chat/create-group',{ | |
uid_str:members | |
}).then(ajaxDoneFilter,ajaxFailFilter).done(function(data){ | |
var conversation = {}; | |
conversation.conversationId = +data.group_info.group_id; | |
conversation.avatarUrl = data.group_info.group_image_url; | |
conversation.title = data.group_info.group_name; | |
conversation.type = Conversation.TYPE_MAP[data.group_info.type]; | |
conversation.unreadCount = 0; | |
conversation.recentMessage = '会话已建立!'; | |
conversation.recentTime = moment().format('YYYY-MM-DD HH:mm:ss'); | |
conversation.isBlocked = +data.is_shielded; | |
conversation.memberList = data.group_info.member_list.map(function(memberItem){ | |
// 对服务端而言,会话的屏蔽状态是针对每个人的 | |
// 但对客户端而言,会话的屏蔽状态不需要管别人 | |
/*if(memberItem.member_uid === window.MyUid){ | |
conversation.isBlocked = +memberItem.is_shielded; | |
}*/ | |
return { | |
uid:+memberItem.member_uid, | |
role:Conversation.ROLE_MAP[memberItem.role], | |
status:Conversation.STATUS_MAP[memberItem.status], | |
avatarUrl:memberItem.member_icon, | |
nickname:memberItem.member_nick, | |
isBlocked:+memberItem.is_shielded | |
}; | |
}); | |
dfd.resolve(new Conversation(conversation)); | |
}).fail(function(msg){ | |
dfd.reject(msg); | |
}); | |
return dfd; | |
}; | |
Conversation.clearConversation = function(){ | |
$messageWrapper.empty(); | |
$messageTitle.text(''); | |
$isBlockedTrue.hide(); | |
$isBlockedFalse.hide(); | |
$conversationDetail.hide(); | |
$chatBox.show(); | |
}; | |
// 更新最后阅读时间 | |
// 使用了一个代理,每10秒钟上报一次 | |
var updateReadTime = function(){ | |
var timeOut = 10*1000; //每10秒上报一次 | |
var timer; | |
var proxyList = {}; //待上报的数据列表 | |
return function(id){ | |
proxyList[id] = Math.floor(Date.now()/1000); | |
if(!timer){ | |
timer = setTimeout(function(){ | |
$.post('/chat/set-time-flag',proxyList,$.noop,'json').done(ajaxDoneFilter,ajaxFailFilter).done(function(data){ | |
proxyList = {}; | |
timer = null; | |
}); | |
},timeOut); | |
} | |
}; | |
}(); | |
function ajaxDoneFilter(data){ | |
var dfd = $.Deferred(); | |
if(+data.result === 0){ | |
dfd.resolve(data.data || data); | |
}else{ | |
dfd.reject(data.ret_msg); | |
} | |
return dfd; | |
} | |
function ajaxFailFilter(xhr,msg,err){ | |
var dfd = $.Deferred(); | |
dfd.reject(msg); | |
return dfd; | |
} | |
function htmlEncode(str){ | |
return str.replace(/</g,'<') | |
.replace(/ /g,' ') | |
.replace(/>/g,'>') | |
.replace(/"/g,'"') | |
.replace(/'/g,'''); | |
} | |
return Conversation; | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<div class="wrap"> | |
<div class="main05 clearBox"> | |
<div class="main05Fleft"> | |
<div class="cBox01"> | |
<div class="t01 t01Bt0"> | |
<h3><span>消息列表</span><!-- <i class="num01">139</i> --></h3> | |
<div class="fr01"> | |
<a class="addBtn01" id="memberPickerTrigger" href="#"><i></i>发起</a> | |
</div> | |
</div> | |
<div class="inboxLeftBox01"> | |
<ul id="listWrapper"> | |
<!-- <li> | |
<a href="#"> | |
<div class="imgBox"> | |
<i class="num01">53</i> | |
<div class="danren"><img width="50" height="50" alt="" src="/icon-100007-120-1369363659?2014.07.28.5.cn"></div> | |
</div> | |
<div class="txt01"> | |
<p class="p01">糊涂的鱼儿</p> | |
<p class="p02 fachu"><i></i>美股通了吗大哥?</p> | |
</div> | |
<span class="time">上午11:02</span> | |
<span title="删除" class="del01"></span> | |
</a> | |
</li> --> | |
</ul> | |
</div> | |
</div> | |
</div> | |
<div class="main05Fright" id="chatBox"> | |
<div class="inboxTopBar01"> | |
<div class="fl" id="messageTitle">暂无会话</div> | |
<div class="fr" id="operateWrapper"> | |
<a href="#" class="quitBtn">退出聊天</a> | |
<a href="#" class="showDetail">查看成员</a> | |
<span class="gapS01">|</span> | |
<a href="#" class="doBlockConversation isBlockedFalse">屏蔽</a> | |
<span class="isBlockedTrue">已屏蔽,<a href="#" class="doUnblockConversation">取消</a></span> | |
</div> | |
</div> | |
<div class="inboxList01" id="messageWrapper"> | |
<!-- <div class="box01 other"> | |
<div class="imgBox"><img width="38" height="38" alt="" src="/icon-100007-120-1369363659?2014.07.28.5.cn"></div> | |
<div class="c01"> | |
<span class="timeBar"><font>刘德华</font><font class="time001">11:32</font></span> | |
<span class="txt01"><i></i>说实话我也不知道要改成什么样子,也没说清楚啊,要想想。</span> | |
</div> | |
</div> | |
<div class="box01 my"> | |
<div class="imgBox"><img width="38" height="38" alt="" src="/icon-100007-120-1369363659?2014.07.28.5.cn"></div> | |
<div class="c01"> | |
<span class="timeBar"><font>刘德华</font><font class="time001">11:32</font></span> | |
<span class="txt01"> | |
<i></i> | |
貌似有大师发生,围观! | |
<div class="fjBar01"> | |
<ul> | |
<li name="attachTxt" class="pdfIcon"><a title="点击下载:开户表格.pdf" href="http://futu.cn/data/attach/201407/31/185/1406808036035877_585_185.pdf" class="tf" target="_blank">开户表格.pdf</a></li> | |
<li name="attachTxt" class="docIcon"><a title="点击下载:熟人开户指引.doc" href="http://futu.cn/data/attach/201407/31/185/1406808036144822_585_185.doc" class="tf" target="_blank">熟人开户指引.doc</a></li> | |
</ul> | |
</div> | |
</span> | |
</div> | |
</div> --> | |
</div> | |
<div class="inboxPostBox01" id="sendWrapper"> | |
<div class="postBox01"> | |
<div class="textareaBox"> | |
<textarea accesskey="u" autocomplete="off" tabindex="1" name="content"></textarea> | |
</div> | |
<div class="upImgBox01" name="upImageBox"> | |
<div key="0" extension="" org_name="" file_id="" class="upImgC01" name="pic_swf_up_row_sample" style="display:none;"> | |
<a onclick="return false;" href="#" class="moveBtn01" name="drag_handle" title="拖动调整图片顺序" onmousedown="return false;"></a> | |
<a onclick="return false;" href="#" class="edCloseBtn01" name="close" title="关闭" onmousedown="return false;"></a> | |
<div style="background-position: -250px 0px;" name="progress" class="uploadBar01"></div> | |
<div name="uploading_txt" class="upImgNameBar">新建位图图像.bmp</div> | |
<div style="display:none;" name="img" class="upImgBox"><img src="" onmousedown="return false;" alt=""></div> | |
<div style="display:none;" name="uploaded_txt" class="upTextarea"><textarea style="display:none;" name="uploaded_img_desc"></textarea></div> | |
</div> | |
</div> | |
<div class="upImgBox01" name="upAttachBox"> | |
<div key="0" extension="" org_name="" file_id="" name="attach_swf_up_row_sample" class="upImgC01" style="display:none;"> | |
<a onclick="return false;" href="#" name="close" class="edCloseBtn01" title="关闭"></a> | |
<div style="background-position: -250px 0px;" class="uploadBar02" name="progress"></div> | |
<div class="uploadBar01Fj" name="complete" style="display:none;"><span style="margin-top:2px; margin-right:2px;vertical-align:top;" class="msgInputOk"></span><span style="display:inline-block; vertical-align:top;">上传完毕</span></div> | |
<div class="upImgNameBarFj"><span class="pdfIcon" name="txt">Hydrangeas.jpg</span></div> | |
</div> | |
</div> | |
<div class="toolBar001"> | |
<div class="fl"> | |
<div class="creatBar"> | |
<div title="插入表情" class="creatBq insertEmotion" name="createBq" onmouseover="$(this).addClass('creatBqC');" onmouseout="$(this).removeClass('creatBqC');" style=""> | |
</div> | |
</div> | |
</div> | |
<div class="fr"> | |
<span class="loading" name="show_posting" style="display:none">正在发表</span> | |
<span class="countTxt" name="tips" style="display:none;"> | |
<span> | |
<font>还能输入</font><em name="count_txt" class="">9</em>字 | |
</span> | |
<span class="orange" name="alert_txt" style="display: none; ">请先填写内容</span> | |
</span> | |
<span class="countTxt"> | |
<span>按 ctrl+enter 发送</span> | |
</span> | |
<a class="button btn01" id="sendMessage" href="#" title="Ctrl+Enter" onclick="return false;" name="ensure">发 表</a> | |
</div> | |
<div class="clear"></div> | |
</div> | |
</div> | |
</div> | |
</div> | |
<div class="main05Fright" id="conversationDetail" style="display:none;"> | |
<div class="inboxTopBar01"> | |
<div class="fl memberCount"><a href="#" class="closeBtn">< 返回</a><font class="gapS01">|</font>查看成员:<span>0</span>人</div> | |
<div class="fr"> | |
<a href="#">退出聊天</a> | |
</div> | |
</div> | |
<div class="inboxUserList01 clearBox"> | |
<ul class="conversationMemberList"> | |
<!-- <li onmouseover="this.className='onmouse01'" onmouseout="this.className=''"> | |
<a href="javascript:void(0);" title="删除" class="edCloseBtn01"></a> | |
<div class="imgBox"><a target="_blank" href="http://bbs.futu5.com/my-100007"><img width="120" height="120" alt="港股美股开户,美股港股交易-富途证券" src="http://bbs.futu5.com/icon-100007-120-1369363659?2014.07.28.5.cn"></a></div> | |
<div class="div01"><a href="http://bbs.futu5.com/my-100007" target="_blank">富途证券</a></div> | |
</li> --> | |
<li> | |
<a class="addBox conversationAddMember" title="添加" href="#"><em></em></a> | |
</li> | |
</ul> | |
</div> | |
<div class="inboxNameModiBar"> | |
<span class="t001">群聊名称:</span> | |
<!-- <div class="c001">杨荣德、刘德华、朱广沪等9人</div> --> | |
<div class="c002"><input type="text" name="textfield" class="conversationName" value="" /></div> | |
</div> | |
<script type="text/tmpl" id="conversationMemberItemTmpl"> | |
{%for(var i=0;i<obj.length;i++){%} | |
<li class="conversationMemberItem" data-uid="{%=obj[i].uid%}"> | |
{%if(window.MyUid !== obj[i].uid){%} | |
<a href="javascript:void(0);" title="删除" class="edCloseBtn01 deleteMember"></a> | |
{%}%} | |
<div class="imgBox"><a target="_blank" href="http://bbs.futu5.com/my-{%=obj[i].uid%}"><img width="120" height="120" alt="{%=obj[i].nickname%}" src="{%=obj[i].avatarUrl%}"></a></div> | |
<div class="div01"><a href="http://bbs.futu5.com/my-{%=obj[i].uid%}" target="_blank">{%=obj[i].nickname%}</a></div> | |
</li> | |
{%}%} | |
</script> | |
</div> | |
</div> | |
</div> | |
<div class="floatBox01" id="memberPicker" style="display:none;"> | |
<div class="alphaDiv01"></div> | |
<div class="m001"> | |
<div class="t01"><h3 name="header">发起聊天</h3><a onclick="return false;" href="#" name="close" class="closeBtn01 closeBtn" title="关闭"></a></div> | |
<div class="c01"> | |
<div class="inboxFloatSearchBar01"><span class="t001">搜索</span><input type="text" placeholder="昵称/牛牛号" class="inputTxt01 gray01 keywords"></div> | |
<div class="inboxFloatSearchList01"> | |
<ul class="memberList"> | |
<!-- <li> | |
<span class="span01"><input type="checkbox" id="noCommission"></span> | |
<span class="imgBox"><img width="38" height="38" alt="王军" src="http://bbs.futu5.com/icon-100007-120-1369363659?2014.07.28.5.cn"/></span> | |
<span class="span02"> | |
<font>Listy</font><font class="font02">110006</font> | |
</span> | |
</li> --> | |
</ul> | |
</div> | |
<div class="inboxFloatSearchBar02 clearBox checkedList"> | |
<!-- <span><a href="javascript:void(0);" title="删除" class="edCloseBtn01"></a><img width="38" height="38" alt="王军" src="http://bbs.futu5.com/icon-100007-120-1369363659?2014.07.28.5.cn"/></span> --> | |
</div> | |
</div> | |
<div class="f01"> | |
<div class="fl checkedCount">共<span>0</span>人</div> | |
<div class="fr"> | |
<a onclick="return false;" id="doSelectMember" name="confirm" href="#" class="button btn01">确 定</a> | |
</div> | |
</div> | |
</div> | |
<script type="text/tmpl" id="memberItemTmpl"> | |
{%for(var i=0;i<obj.length;i++){%} | |
<li data-uid="{%=obj[i].uid%}" data-avatarurl="{%=obj[i].avatarUrl%}" data-nickname="{%=obj[i].nickname%}"> | |
<span class="span01"><input type="checkbox"></span> | |
<span class="imgBox"><img width="38" height="38" alt="{%=obj[i].nickname%}" src="{%=obj[i].avatarUrl%}"/></span> | |
<span class="span02"> | |
<font>{%=obj[i].nickname%}</font><font class="font02">{%=obj[i].uid%}</font> | |
</span> | |
</li> | |
{%}%} | |
</script> | |
<script type="text/tmpl" id="checkedMemberItemTmpl"> | |
{%for(var i=0;i<obj.length;i++){%} | |
<span class="checkedItem" data-uid="{%=obj[i].uid%}"> | |
<a href="javascript:void(0);" title="删除" class="edCloseBtn01 uncheckMember"></a> | |
<img width="38" height="38" alt="{%=obj[i].nickname%}" src="{%=obj[i].avatarUrl%}"/> | |
</span> | |
{%}%} | |
</script> | |
</div> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
define([ | |
'underscore.min', | |
'jquery', | |
'app/futu5/chat/conversation' | |
],function(_,$,Conversation){ | |
var listItemTmpl = $('#listItemTmpl').html(); | |
var $listWrapper = $('#listWrapper'); | |
var list = {}; | |
var conversationList = []; | |
var renderListFunc = _.template(listItemTmpl); | |
list.renderList = function(renderList){ | |
if(!renderList){ | |
renderList = conversationList; | |
} | |
renderList.sort(function(conversation1,conversation2){ | |
return conversation1.recentTime > conversation2.recentTime?-1:1; | |
}); | |
var html = renderList.map(function(item){ | |
return renderListFunc(item); | |
}); | |
$listWrapper.html(html); | |
}; | |
list.getList = function(){ | |
return $.getJSON('/chat/my-group-list').then(ajaxDoneFilter,ajaxFailFilter); | |
}; | |
list.updateList = function(){ | |
this.getList().done(function(data){ | |
conversationList = data.group_list.map(function(dataItem){ | |
// var conversationData = dataItem; | |
var conversationData = {}; | |
conversationData.recentMessage = dataItem.last_message; | |
conversationData.recentTime = dataItem.last_update_time; | |
conversationData.title = dataItem.group_name; | |
conversationData.conversationId = +dataItem.group_id; | |
conversationData.avatarUrl = dataItem.group_image_url; | |
conversationData.isBlocked = +dataItem.is_shielded; | |
conversationData.unreadCount = +dataItem.unread_messages; | |
conversationData.isActive = false; | |
conversationData.memberList = dataItem.member_list.map(function(memberItem){ | |
return { | |
uid:+memberItem.member_uid, | |
role:Conversation.ROLE_MAP[memberItem.role], | |
status:Conversation.STATUS_MAP[memberItem.status], | |
avatarUrl:memberItem.member_icon, | |
nickname:memberItem.member_nick, | |
}; | |
}); | |
return new Conversation(conversationData); | |
}); | |
if(conversationList[0]){ | |
conversationList[0].active(); | |
}else{ | |
Conversation.clearMessageList(); | |
} | |
list.renderList(conversationList); | |
}).fail(function(msg){ | |
console.log('fail',msg); | |
}); | |
}; | |
list.appendConversation = function(conversation){ | |
conversationList.push(conversation); | |
}; | |
list.prependConversation = function(conversation){ | |
conversationList.unshift(conversation); | |
}; | |
list.getConversationById = function(conversationId){ | |
var targetConversation; | |
conversationList.forEach(function(conversationItem){ | |
if(conversationItem.conversationId === conversationId){ | |
targetConversation = conversationItem; | |
} | |
}); | |
return targetConversation; | |
}; | |
list.bindEvents = function(){ | |
$listWrapper.on('click','.list-item',function(){ | |
var targetConversation; | |
var conversationId = +$(this).data('conversationid'); | |
conversationList.forEach(function(conversationItem){ | |
if(conversationItem.conversationId === conversationId){ | |
targetConversation = conversationItem; | |
} | |
}); | |
targetConversation.active(); | |
return false; | |
}); | |
// 从列表中删除某个会话 | |
$listWrapper.on('click','.list-item .delConversation',function(e){ | |
var targetConversation; | |
var conversationId = +$(this).closest('li').data('conversationid'); | |
conversationList.forEach(function(conversationItem){ | |
if(conversationItem.conversationId === conversationId){ | |
targetConversation = conversationItem; | |
} | |
}); | |
targetConversation.delete(); | |
return false; | |
}); | |
// 接收Conversation的事件,处理列表 | |
/*$(window).on('Conversation_Delete',function(e,conversationId){ | |
});*/ | |
// 接收Conversation删除、退出的事件,重新选取会话进行聚焦 | |
$(window).on('Conversation_Delete Conversation_Quit',function(e,conversationId){ | |
conversationList.forEach(function(conversationItem,index){ | |
if(conversationItem.conversationId === conversationId){ | |
conversationList.splice(index,1); | |
} | |
}); | |
if(conversationList.length){ | |
conversationList[0].active(); | |
}else{ | |
Conversation.clearConversation(); | |
} | |
list.renderList(); | |
}); | |
// 接收Conversation聚焦(激活)事件,处理左侧高亮 | |
$(window).on('Conversation_Active',function(e,conversationId){ | |
conversationList.forEach(function(conversationItem){ | |
if(conversationItem.conversationId === conversationId){ | |
conversationItem.isActive = true; | |
}else{ | |
conversationItem.isActive = false; | |
} | |
}); | |
list.renderList(); | |
}); | |
// 接收Conversation更新事件,刷新列表 | |
$(window).on('Conversation_Update',function(e,conversationId){ | |
list.renderList(); | |
}); | |
}; | |
function ajaxDoneFilter(data){ | |
var dfd = $.Deferred(); | |
if(+data.result === 0){ | |
dfd.resolve(data.data || data); | |
}else{ | |
dfd.reject(data.ret_msg); | |
} | |
return dfd; | |
} | |
function ajaxFailFilter(xhr,msg,err){ | |
var dfd = $.Deferred(); | |
dfd.reject(msg); | |
return dfd; | |
} | |
return list; | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require([ | |
'jquery', | |
'underscore.min', | |
'app/common/comet', | |
'app/futu5/chat/list', | |
'app/futu5/chat/conversation', | |
'app/futu5/chat/memberpicker' | |
],function($,_,Comet,list,Conversation,memberPicker){ | |
list.updateList(); | |
list.bindEvents(); | |
Conversation.bindEvents(); | |
$('#memberPickerTrigger').click(function(){ | |
memberPicker.init({ | |
onSelect:function(memberList){ | |
Conversation.createConversation('group',memberList.map(function(memberItem){ | |
return memberItem.uid; | |
}).join(',')).done(function(conversation){ | |
list.prependConversation(conversation); | |
conversation.active(); | |
list.renderList(); | |
memberPicker.hide(); | |
}).fail(function(){ | |
}); | |
} | |
}); | |
return false; | |
}); | |
// 聊天信息长连接 | |
var chatConnection = new Comet({ | |
url:'/chat/get-message', | |
method:'post', | |
onData:function(data){ | |
console.log(data); | |
for(var conversationId in data.message_list){ | |
var targetConversation = list.getConversationById(+conversationId); | |
// 新会话,先建立一个占位,然后去更新它 | |
if(!targetConversation){ | |
targetConversation = new Conversation({ | |
conversationId: +conversationId, | |
avatarUrl: '', | |
title:'', | |
type:'', | |
unreadCount:1, | |
recentMessage:'', | |
recentTime: '', | |
isBlocked:false, | |
isActive:false, | |
memberList:[], | |
_isFake:true //表示从前端创建的会话 | |
}); | |
list.prependConversation(targetConversation); | |
targetConversation.update(); | |
} | |
data.message_list[conversationId].message_list.forEach(function(message){ | |
console.log(message); | |
var messageToPush = { | |
avatarUrl:message.member_icon, | |
nickname:message.member_nick, | |
role:+message.from_uid === window.MyUid ? 'my':'other', | |
userId:message.from_uid, | |
time:message.create_time, | |
content:message.content, | |
type:Conversation.MESSAGE_TYPE_MAP[message.msg_type], | |
extra:message.extra | |
}; | |
targetConversation.pushMessage(messageToPush); | |
targetConversation.dealFeeds(messageToPush); | |
if(message.from_uid === window.MyUid && !Conversation.me){ | |
// 如果是自己的消息,并且没有个人信息,则需要渲染并填充个人信息 | |
Conversation.me = { | |
avatarUrl:message.member_icon, | |
nickname:message.member_nick | |
}; | |
} | |
if(targetConversation.recentTime < message.create_time){ | |
targetConversation.recentTime = message.create_time; | |
targetConversation.recentMessage = message.content; | |
if(targetConversation._isFake){ | |
targetConversation.title = 'Loading...'; | |
} | |
} | |
}); | |
} | |
list.renderList(); | |
} | |
}); | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
define([ | |
'underscore.min', | |
'jquery' | |
],function(_,$){ | |
var memberPicker = {}; | |
var options = {}; | |
var memberItemTmpl = $('#memberPicker #memberItemTmpl').html(); | |
var memberItemFunc = _.template(memberItemTmpl); | |
var checkedMemberList = []; | |
var checkedMemberItemTmpl = $('#memberPicker #checkedMemberItemTmpl').html(); | |
var checkedMemberItemFunc = _.template(checkedMemberItemTmpl); | |
memberPicker.init = function(opts){ | |
var defaultOptions = { | |
onSelect:$.noop, | |
onCancel:$.noop | |
}; | |
options = $.extend({},defaultOptions,opts); | |
if(!$('#memberPicker').data('inited')){ | |
bindEvents(); | |
} | |
this.show(); | |
}; | |
memberPicker.show = function(){ | |
$('#memberPicker .keywords').val(''); | |
$('#memberPicker .memberList').empty(); | |
$('#memberPicker .checkedList').empty(); | |
$('#memberPicker .checkedCount span').text('0'); | |
checkedMemberList = []; | |
showDialog(); | |
}; | |
memberPicker.hide = function(){ | |
$('#memberPicker').fadeOut(200); | |
}; | |
function showDialog(){ | |
$('#memberPicker').data('inited',true).fadeIn(200); | |
} | |
function searchMember(keyword){ | |
return $.getJSON('/chat/search-user',{ | |
keyword:keyword | |
}).then(ajaxDoneFilter,ajaxFailFilter); | |
} | |
function bindEvents(){ | |
$('#memberPicker .closeBtn').click(function(){ | |
$('#memberPicker').fadeOut(200); | |
}); | |
$('#memberPicker .keywords').on('input textinput paste propertychange',function(){ | |
var value = $(this).val(); | |
if(!value) return; | |
searchMember(value).done(function(data){ | |
var memberArr = data.map(function(memberItem){ | |
return { | |
uid:memberItem.id, | |
nickname:memberItem.nick, | |
avatarUrl:memberItem.icon | |
}; | |
}); | |
var html = memberItemFunc(memberArr); | |
$('#memberPicker .memberList').html(html); | |
}).fail(function(){ | |
}); | |
}); | |
$('#memberPicker').on('change','.memberList input[type=checkbox]',function(){ | |
var checked = $(this).prop('checked'); | |
var item = $(this).closest('li'); | |
var itemData = { | |
uid:+item.data('uid'), | |
avatarUrl:item.data('avatarurl'), | |
nickname:item.data('nickname') | |
}; | |
if(checked){ | |
checkedMemberList.push(itemData); | |
}else{ | |
checkedMemberList.forEach(function(item,index){ | |
if(item.uid === itemData.uid){ | |
checkedMemberList.splice(index,1); | |
} | |
}); | |
} | |
var html = checkedMemberItemFunc(checkedMemberList); | |
$('#memberPicker .checkedList').html(html); | |
$('#memberPicker .checkedCount span').text(checkedMemberList.length); | |
}); | |
$('#memberPicker').on('click','.checkedList .uncheckMember',function(){ | |
var uid = $(this).closest('.checkedItem').data('uid'); | |
checkedMemberList.forEach(function(item,index){ | |
if(item.uid === uid){ | |
checkedMemberList.splice(index,1); | |
} | |
}); | |
$('#memberPicker input[type=checkbox]').each(function(){ | |
var $this = $(this); | |
if($this.closest('li').data('uid') === uid){ | |
$this.prop('checked',false); | |
} | |
}); | |
var html = checkedMemberItemFunc(checkedMemberList); | |
$('#memberPicker .checkedList').html(html); | |
$('#memberPicker .checkedCount span').text(checkedMemberList.length); | |
}); | |
$('#memberPicker #doSelectMember').click(function(){ | |
options.onSelect(checkedMemberList); | |
}); | |
} | |
function ajaxDoneFilter(data){ | |
var dfd = $.Deferred(); | |
if(+data.result === 0){ | |
dfd.resolve(data.user_list); | |
}else{ | |
dfd.reject(data.ret_msg); | |
} | |
return dfd; | |
} | |
function ajaxFailFilter(xhr,msg,err){ | |
var dfd = $.Deferred(); | |
dfd.reject(msg); | |
return dfd; | |
} | |
return memberPicker; | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment