whisper.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587
  1. var uinfo = {
  2. id: 'KF' + uid,
  3. username: uname,
  4. avatar: avatar,
  5. group: group
  6. };
  7. // 创建一个Socket实例
  8. var socket = new WebSocket('ws://' + socket_server+'?apiToken=72fb0848e7bf20e052362bac4a86164c');
  9. // 打开Socket
  10. socket.onopen = function (res) {
  11. layui.use(['layer'], function () {
  12. var layer = layui.layer;
  13. layer.ready(function () {
  14. layer.msg('链接成功', {time: 1000});
  15. });
  16. });
  17. // 登录
  18. var login_data = '{"type":"init", "uid":"' + uinfo.id + '", "name" : "' + uinfo.username + '", "avatar" : "'
  19. + uinfo.avatar + '", "group": ' + uinfo.group + '}';
  20. socket.send(login_data);
  21. };
  22. // 监听消息
  23. socket.onmessage = function (res) {
  24. var data = eval("(" + res.data + ")");
  25. switch (data['message_type']) {
  26. // 服务端ping客户端
  27. case 'ping':
  28. socket.send('{"type":"ping"}');
  29. break;
  30. // 添加用户
  31. case 'connect':
  32. addUser(data.data.user_info);
  33. break;
  34. // 移除访客到主面板
  35. case 'delUser':
  36. delUser(data.data);
  37. break;
  38. // 监测聊天数据
  39. case 'chatMessage':
  40. showUserMessage(data.data, data.data.content);
  41. break;
  42. }
  43. };
  44. // 监听失败
  45. socket.onerror = function(err){
  46. layer.alert('连接失败,请联系管理员', {icon: 2, title: '错误提示'});
  47. };
  48. $(function () {
  49. // 获取服务用户列表
  50. $.getJSON('/service/index/getUserList', function(res){
  51. if(1 == res.code && res.data.length > 0){
  52. $.each(res.data, function(k, v){
  53. addUser(v);
  54. });
  55. var id = $(".layui-unselect").find('li').eq(0).data('id');
  56. var name = $(".layui-unselect").find('li').eq(0).data('name');
  57. var avatar = $(".layui-unselect").find('li').eq(0).data('avatar');
  58. var ip = $(".layui-unselect").find('li').eq(0).data('ip');
  59. // 默认设置第一个用户为当前对话的用户
  60. $("#active-user").attr('data-id', id).attr('data-name', name).attr('data-avatar', avatar).attr('data-ip', ip);
  61. $(".layui-unselect").find('li').eq(0).addClass('active').find('span:eq(1)').removeClass('layui-badge').text('');
  62. $("#f-user").val(name);
  63. $("#f-ip").val(ip);
  64. $.getJSON('/service/index/getCity', {ip: ip}, function(res){
  65. $("#f-area").val(res.data);
  66. });
  67. // 拉取和这个人的聊天记录
  68. $("#u-" + id).show();
  69. getChatLog(id, 1);
  70. }
  71. });
  72. // 监听快捷键发送
  73. document.getElementById('msg-area').addEventListener('keydown', function (e) {
  74. if (e.keyCode != 13) return;
  75. e.preventDefault(); // 取消事件的默认动作
  76. sendMessage();
  77. });
  78. // 点击表情
  79. var index;
  80. $("#face").click(function (e) {
  81. e.stopPropagation();
  82. layui.use(['layer'], function () {
  83. var layer = layui.layer;
  84. var isShow = $(".layui-whisper-face").css('display');
  85. if ('block' == isShow) {
  86. layer.close(index);
  87. return;
  88. }
  89. var height = $(".chat-box").height() - 110;
  90. layer.ready(function () {
  91. index = layer.open({
  92. type: 1,
  93. offset: [height + 'px', $(".layui-side").width() + 'px'],
  94. shade: false,
  95. title: false,
  96. closeBtn: 0,
  97. area: '395px',
  98. content: showFaces()
  99. });
  100. });
  101. });
  102. });
  103. $(document).click(function (e) {
  104. layui.use(['layer'], function () {
  105. var layer = layui.layer;
  106. if (isShow) {
  107. layer.close(index);
  108. return false;
  109. }
  110. });
  111. });
  112. // 发送消息
  113. $("#send").click(function () {
  114. sendMessage();
  115. });
  116. // hover用户
  117. $(".layui-unselect li").hover(function () {
  118. $(this).find('i').show();
  119. }, function () {
  120. $(this).find('i').hide();
  121. });
  122. // 关闭用户
  123. $('.close').click(function () {
  124. var uid = $(this).parent().data('id');
  125. $(this).parent().remove(); // 清除左侧的用户列表
  126. $('#u-' + uid).remove(); // 清除右侧的聊天详情
  127. });
  128. // 检测滚动,异步加载更多聊天数据
  129. $(".chat-box").scroll(function () {
  130. var top = $(".chat-box").scrollTop();
  131. });
  132. // 会员转接
  133. $("#scroll-link").click(function(){
  134. var id = $("#active-user").attr('data-id');
  135. var name = $("#active-user").attr('data-name');
  136. var avatar = $("#active-user").attr('data-avatar');
  137. var ip = $("#active-user").attr('data-ip');
  138. if(id == '' || name == ''){
  139. layer.msg("请选择要转接的会员");
  140. }
  141. // 二次确认
  142. var layerIndex = null;
  143. layerIndex = layer.confirm('确定转接 ' + name + ' ?', {
  144. title: '转接提示',
  145. closeBtn: 0,
  146. icon: 3,
  147. btn: ['确定', '取消'] // 按钮
  148. }, function(){
  149. layer.close(layerIndex);
  150. layerIndex = layer.open({
  151. title: '',
  152. type: 1,
  153. area: ['30%', '40%'],
  154. content: $("#change-box")
  155. });
  156. // 监听选择
  157. layui.use(['form'], function(){
  158. var form = layui.form;
  159. form.on('select(group)', function (data) {
  160. if(uinfo.group == data.value){
  161. layer.msg("已经在该分组,不需要转接!");
  162. }else{
  163. layer.close(layerIndex);
  164. var group = data.value; // 分组
  165. // 交换分组
  166. var change_data = '{"type":"changeGroup", "uid":"' + id + '", "name" : "' + name + '", "avatar" : "'
  167. + avatar + '", "group": ' + group + ', "ip" : "' + ip + '"}';
  168. //console.log(change_data);
  169. socket.send(change_data);
  170. // 将该会员从我的会话中移除
  171. delUser({id: id});
  172. layer.msg('转接成功');
  173. }
  174. });
  175. });
  176. }, function(){
  177. });
  178. });
  179. });
  180. var isShow = false;
  181. layui.use(['element', 'form'], function () {
  182. var element = layui.element;
  183. var form = layui.form;
  184. });
  185. // 图片 文件上传
  186. layui.use(['upload', 'layer'], function () {
  187. var upload = layui.upload;
  188. var layer = layui.layer;
  189. // 执行实例
  190. var uploadInstImg = upload.render({
  191. elem: '#image' // 绑定元素
  192. , accept: 'images'
  193. , exts: 'jpg|jpeg|png|gif'
  194. , url: '/service/upload/uploadImg' // 上传接口
  195. , done: function (res) {
  196. sendMessage('img[' + res.data.src + ']');
  197. showBigPic();
  198. }
  199. , error: function () {
  200. // 请求异常回调
  201. }
  202. });
  203. var uploadInstFile = upload.render({
  204. elem: '#file' // 绑定元素
  205. , accept: 'file'
  206. , exts: 'zip|rar'
  207. , url: '/service/upload/uploadFile' // 上传接口
  208. , done: function (res) {
  209. sendMessage('file(' + res.data.src + ')[' + res.msg + ']');
  210. }
  211. , error: function () {
  212. // 请求异常回调
  213. }
  214. });
  215. });
  216. // 展示表情数据
  217. function showFaces() {
  218. isShow = true;
  219. var alt = getFacesIcon();
  220. var _html = '<div class="layui-whisper-face"><ul class="layui-clear whisper-face-list">';
  221. layui.each(alt, function (index, item) {
  222. _html += '<li title="' + item + '" onclick="checkFace(this)"><img src="/static/service/js/layui/images/face/' + index + '.gif" /></li>';
  223. });
  224. _html += '</ul></div>';
  225. return _html;
  226. }
  227. // 选择表情
  228. function checkFace(obj) {
  229. var word = $(".msg-area").val() + ' face' + $(obj).attr('title') + ' ';
  230. $(".msg-area").val(word).focus();
  231. }
  232. // 发送消息
  233. function sendMessage(sendMsg) {
  234. var msg = (typeof(sendMsg) == 'undefined') ? $(".msg-area").val() : sendMsg;
  235. if ('' == msg) {
  236. layui.use(['layer'], function () {
  237. var layer = layui.layer;
  238. return layer.msg('请输入回复内容', {time: 1000});
  239. });
  240. return false;
  241. }
  242. var word = msgFactory(msg, 'mine', uinfo);
  243. var uid = $("#active-user").attr('data-id');
  244. var uname = $("#active-user").attr('data-name');
  245. socket.send(JSON.stringify({
  246. type: 'chatMessage',
  247. data: {to_id: uid, to_name: uname, content: msg, from_name: uinfo.username,
  248. from_id: uinfo.id, from_avatar: uinfo.avatar}
  249. }));
  250. $("#u-" + uid).append(word);
  251. $(".msg-area").val('');
  252. // 滚动条自动定位到最底端
  253. wordBottom();
  254. }
  255. // 展示客服发送来的消息
  256. function showUserMessage(uinfo, content) {
  257. if ($('#f-' + uinfo.id).length == 0) {
  258. addUser(uinfo);
  259. }
  260. // 未读条数计数
  261. if (!$('#f-' + uinfo.id).hasClass('active')) {
  262. var num = $('#f-' + uinfo.id).find('span:eq(1)').text();
  263. if (num == '') num = 0;
  264. num = parseInt(num) + 1;
  265. $('#f-' + uinfo.id).find('span:eq(1)').removeClass('layui-badge').addClass('layui-badge').text(num);
  266. }
  267. var word = msgFactory(content, 'user', uinfo);
  268. setTimeout(function () {
  269. $("#u-" + uinfo.id).append(word);
  270. // 滚动条自动定位到最底端
  271. wordBottom();
  272. showBigPic();
  273. }, 200);
  274. }
  275. // 消息发送工厂
  276. function msgFactory(content, type, uinfo) {
  277. var _html = '';
  278. if ('mine' == type) {
  279. _html += '<li class="whisper-chat-mine">';
  280. } else {
  281. _html += '<li>';
  282. }
  283. _html += '<div class="whisper-chat-user">';
  284. _html += '<img src="' + uinfo.avatar + '">';
  285. if ('mine' == type) {
  286. _html += '<cite><i>' + getDate() + '</i>' + uinfo.username + '</cite>';
  287. } else {
  288. _html += '<cite>' + uinfo.name + '<i>' + getDate() + '</i></cite>';
  289. }
  290. _html += '</div><div class="whisper-chat-text">' + replaceContent(content) + '</div>';
  291. _html += '</li>';
  292. return _html;
  293. }
  294. // 获取日期
  295. function getDate() {
  296. var d = new Date(new Date());
  297. return d.getFullYear() + '-' + digit(d.getMonth() + 1) + '-' + digit(d.getDate())
  298. + ' ' + digit(d.getHours()) + ':' + digit(d.getMinutes()) + ':' + digit(d.getSeconds());
  299. }
  300. //补齐数位
  301. var digit = function (num) {
  302. return num < 10 ? '0' + (num | 0) : num;
  303. };
  304. // 滚动条自动定位到最底端
  305. function wordBottom() {
  306. var box = $(".chat-box");
  307. box.scrollTop(box[0].scrollHeight);
  308. }
  309. // 切换在线用户
  310. function changeUserTab(obj) {
  311. obj.addClass('active').siblings().removeClass('active');
  312. wordBottom();
  313. }
  314. // 添加用户到面板
  315. function addUser(data) {
  316. var _html = '<li class="layui-nav-item" data-id="' + data.id + '" id="f-' + data.id +
  317. '" data-name="' + data.name + '" data-avatar="' + data.avatar + '" data-ip="' + data.ip + '">';
  318. _html += '<img src="' + data.avatar + '">';
  319. _html += '<span class="user-name">' + data.name + '</span>';
  320. _html += '<span class="layui-badge" style="margin-left:5px">0</span>';
  321. _html += '<i class="layui-icon close" style="display:none">ဇ</i>';
  322. _html += '</li>';
  323. // 添加左侧列表
  324. $("#user_list").append(_html);
  325. // 如果没有选中人,选中第一个
  326. var hasActive = 0;
  327. $("#user_list li").each(function(){
  328. if($(this).hasClass('active')){
  329. hasActive = 1;
  330. }
  331. });
  332. var _html2 = '';
  333. _html2 += '<ul id="u-' + data.id + '">';
  334. _html2 += '</ul>';
  335. // 添加主聊天面板
  336. $('.chat-box').append(_html2);
  337. if(0 == hasActive){
  338. $("#user_list").find('li').eq(0).addClass('active').find('span:eq(1)').removeClass('layui-badge').text('');
  339. $("#u-" + data.id).show();
  340. var id = $(".layui-unselect").find('li').eq(0).data('id');
  341. var name = $(".layui-unselect").find('li').eq(0).data('name');
  342. var ip = $(".layui-unselect").find('li').eq(0).data('ip');
  343. var avatar = $(".layui-unselect").find('li').eq(0).data('avatar');
  344. // 设置当前会话用户
  345. $("#active-user").attr('data-id', id).attr('data-name', name).attr('data-avatar', avatar).attr('data-ip', ip);
  346. $("#f-user").val(name);
  347. $("#f-ip").val(ip);
  348. $.getJSON('/service/index/getCity', {ip: ip}, function(res){
  349. $("#f-area").val(res.data);
  350. });
  351. }
  352. getChatLog(data.id, 1);
  353. checkUser();
  354. }
  355. // 操作新连接用户的 dom操作
  356. function checkUser() {
  357. $(".layui-unselect").find('li').unbind("click"); // 防止事件叠加
  358. // 切换用户
  359. $(".layui-unselect").find('li').bind('click', function () {
  360. changeUserTab($(this));
  361. var uid = $(this).data('id');
  362. var avatar = $(this).data('avatar');
  363. var name = $(this).data('name');
  364. var ip = $(this).data('ip');
  365. // 展示相应的对话信息
  366. $('.chat-box ul').each(function () {
  367. if ('u-' + uid == $(this).attr('id')) {
  368. $(this).addClass('show-chat-detail').siblings().removeClass('show-chat-detail').attr('style', '');
  369. return false;
  370. }
  371. });
  372. // 去除消息提示
  373. $(this).find('span').eq(1).removeClass('layui-badge').text('');
  374. // 设置当前会话的用户
  375. $("#active-user").attr('data-id', uid).attr('data-name', name).attr('data-avatar', avatar).attr('data-ip', ip);
  376. // 右侧展示详情
  377. $("#f-user").val(name);
  378. $("#f-ip").val(ip);
  379. $.getJSON('/service/index/getCity', {ip: ip}, function(res){
  380. $("#f-area").val(res.data);
  381. });
  382. getChatLog(uid, 1);
  383. wordBottom();
  384. });
  385. }
  386. // 删除用户聊天面板
  387. function delUser(data) {
  388. $("#f-" + data.id).remove(); // 清除左侧的用户列表
  389. $('#u-' + data.id).remove(); // 清除右侧的聊天详情
  390. }
  391. // 发送快捷语句
  392. function sendWord(obj) {
  393. var msg = $(obj).data('word');
  394. sendMessage(msg);
  395. }
  396. // 获取聊天记录
  397. function getChatLog(uid, page, flag) {
  398. $.getJSON('/service/index/getChatLog', {uid: uid, page: page}, function(res){
  399. if(1 == res.code && res.data.length > 0){
  400. if(res.msg == res.total){
  401. var _html = '<div class="layui-flow-more">没有更多了</div>';
  402. }else{
  403. var _html = '<div class="layui-flow-more"><a href="javascript:;" data-page="' + parseInt(res.msg + 1)
  404. + '" onclick="getMore(this)"><cite>更多记录</cite></a></div>';
  405. }
  406. var len = res.data.length;
  407. for(var i = 0; i < len; i++){
  408. var v = res.data[len - i - 1];
  409. if ('mine' == v.type) {
  410. _html += '<li class="whisper-chat-mine">';
  411. } else {
  412. _html += '<li>';
  413. }
  414. _html += '<div class="whisper-chat-user">';
  415. _html += '<img src="' + v.from_avatar + '">';
  416. if ('mine' == v.type) {
  417. _html += '<cite><i>' + v.time_line + '</i>' + v.from_name + '</cite>';
  418. } else {
  419. _html += '<cite>' + v.from_name + '<i>' + v.time_line + '</i></cite>';
  420. }
  421. _html += '</div><div class="whisper-chat-text">' + replaceContent(v.content) + '</div>';
  422. _html += '</li>';
  423. }
  424. setTimeout(function () {
  425. // 滚动条自动定位到最底端
  426. if(typeof flag == 'undefined'){
  427. $("#u-" + uid).html(_html);
  428. wordBottom();
  429. }else{
  430. $("#u-" + uid).prepend(_html);
  431. }
  432. showBigPic();
  433. }, 100);
  434. }
  435. });
  436. }
  437. // 显示大图
  438. function showBigPic(){
  439. $(".layui-whisper-photos").on('click', function () {
  440. var src = this.src;
  441. layer.photos({
  442. photos: {
  443. data: [{
  444. "alt": "大图模式",
  445. "src": src
  446. }]
  447. }
  448. , shade: 0.5
  449. , closeBtn: 2
  450. , anim: 0
  451. , resize: false
  452. , success: function (layero, index) {
  453. }
  454. });
  455. });
  456. }
  457. // 获取更多的的记录
  458. function getMore(obj){
  459. $(obj).remove();
  460. var page = $(obj).attr('data-page');
  461. var uid = $(".layui-unselect").find('li').eq(0).data('id');
  462. getChatLog(uid, page, 1);
  463. }
  464. // 打卡下班
  465. function loginOut(){
  466. layer.msg("正在关闭,未咨询完的用户", {time: 50000});
  467. var len = $("#user_list li").length;
  468. var closeNum = 0;
  469. if(len == closeNum){
  470. window.location.href = '/service/login/loginOut';
  471. }
  472. $("#user_list li").each(function(){
  473. var uid = $(this).data('id');
  474. var activeUid = $("#active-user").attr('data-id');
  475. if(uid == activeUid){
  476. $("#active-user").attr('data-id', -999);
  477. }
  478. socket.send(JSON.stringify({
  479. type: 'closeUser', uid: uid
  480. }));
  481. $(this).parent().remove(); // 清除左侧的用户列表
  482. $('#u-' + uid).remove(); // 清除右侧的聊天详情
  483. closeNum++;
  484. if(closeNum == len){
  485. setTimeout(function(){
  486. window.location.href = '/service/login/loginOut';
  487. }, 1500); // 此处等待用户真的退出了
  488. }
  489. });
  490. }