whisper.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620
  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=8444fb09eec653b322a4ad74b836aed0');
  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, conversationId: 1}
  249. }));
  250. $("#u-" + uid).append(word);
  251. $(".msg-area").val('');
  252. // 滚动条自动定位到最底端
  253. wordBottom();
  254. }
  255. // 关闭工单
  256. function cc() {
  257. var uid = $("#active-user").attr('data-id');
  258. socket.send(JSON.stringify({
  259. type: 'kfCloseUser',
  260. data: {to_id: uid}
  261. }));
  262. }
  263. // 上线
  264. function dd() {
  265. var uid = $("#active-user").attr('data-id');
  266. socket.send(JSON.stringify({
  267. type: 'kfOnline',
  268. data: {status: 1,uid: uinfo.id}
  269. }));
  270. }
  271. // 拒接
  272. function ee() {
  273. var uid = $("#active-user").attr('data-id');
  274. socket.send(JSON.stringify({
  275. type: 'kfOnline',
  276. data: {status: 2,uid: uinfo.id}
  277. }));
  278. }
  279. // 展示客服发送来的消息
  280. function showUserMessage(uinfo, content) {
  281. if ($('#f-' + uinfo.id).length == 0) {
  282. addUser(uinfo);
  283. }
  284. // 未读条数计数
  285. if (!$('#f-' + uinfo.id).hasClass('active')) {
  286. var num = $('#f-' + uinfo.id).find('span:eq(1)').text();
  287. if (num == '') num = 0;
  288. num = parseInt(num) + 1;
  289. $('#f-' + uinfo.id).find('span:eq(1)').removeClass('layui-badge').addClass('layui-badge').text(num);
  290. }
  291. var word = msgFactory(content, 'user', uinfo);
  292. setTimeout(function () {
  293. $("#u-" + uinfo.id).append(word);
  294. // 滚动条自动定位到最底端
  295. wordBottom();
  296. showBigPic();
  297. }, 200);
  298. }
  299. // 消息发送工厂
  300. function msgFactory(content, type, uinfo) {
  301. var _html = '';
  302. if ('mine' == type) {
  303. _html += '<li class="whisper-chat-mine">';
  304. } else {
  305. _html += '<li>';
  306. }
  307. _html += '<div class="whisper-chat-user">';
  308. _html += '<img src="' + uinfo.avatar + '">';
  309. if ('mine' == type) {
  310. _html += '<cite><i>' + getDate() + '</i>' + uinfo.username + '</cite>';
  311. } else {
  312. _html += '<cite>' + uinfo.name + '<i>' + getDate() + '</i></cite>';
  313. }
  314. _html += '</div><div class="whisper-chat-text">' + replaceContent(content) + '</div>';
  315. _html += '</li>';
  316. return _html;
  317. }
  318. // 获取日期
  319. function getDate() {
  320. var d = new Date(new Date());
  321. return d.getFullYear() + '-' + digit(d.getMonth() + 1) + '-' + digit(d.getDate())
  322. + ' ' + digit(d.getHours()) + ':' + digit(d.getMinutes()) + ':' + digit(d.getSeconds());
  323. }
  324. //补齐数位
  325. var digit = function (num) {
  326. return num < 10 ? '0' + (num | 0) : num;
  327. };
  328. // 滚动条自动定位到最底端
  329. function wordBottom() {
  330. var box = $(".chat-box");
  331. box.scrollTop(box[0].scrollHeight);
  332. }
  333. // 切换在线用户
  334. function changeUserTab(obj) {
  335. obj.addClass('active').siblings().removeClass('active');
  336. wordBottom();
  337. }
  338. // 添加用户到面板
  339. function addUser(data) {
  340. console.log(data)
  341. var _html = '<li class="layui-nav-item" data-id="' + data.id + '" id="f-' + data.id +
  342. '" data-name="' + data.name + '" data-avatar="' + data.avatar + '" data-ip="' + data.ip + '">';
  343. _html += '<img src="' + data.avatar + '">';
  344. _html += '<span class="user-name">' + data.name + '</span>';
  345. _html += '<span class="layui-badge" style="margin-left:5px">0</span>';
  346. _html += '<i class="layui-icon close" style="display:none">ဇ</i>';
  347. _html += '</li>';
  348. // 添加左侧列表
  349. $("#user_list").append(_html);
  350. // 如果没有选中人,选中第一个
  351. var hasActive = 0;
  352. $("#user_list li").each(function(){
  353. if($(this).hasClass('active')){
  354. hasActive = 1;
  355. }
  356. });
  357. var _html2 = '';
  358. _html2 += '<ul id="u-' + data.id + '">';
  359. _html2 += '</ul>';
  360. // 添加主聊天面板
  361. $('.chat-box').append(_html2);
  362. if(0 == hasActive){
  363. $("#user_list").find('li').eq(0).addClass('active').find('span:eq(1)').removeClass('layui-badge').text('');
  364. $("#u-" + data.id).show();
  365. var id = $(".layui-unselect").find('li').eq(0).data('id');
  366. var name = $(".layui-unselect").find('li').eq(0).data('name');
  367. var ip = $(".layui-unselect").find('li').eq(0).data('ip');
  368. var avatar = $(".layui-unselect").find('li').eq(0).data('avatar');
  369. // 设置当前会话用户
  370. $("#active-user").attr('data-id', id).attr('data-name', name).attr('data-avatar', avatar).attr('data-ip', ip);
  371. $("#f-user").val(name);
  372. $("#f-ip").val(ip);
  373. $.getJSON('/service/index/getCity', {ip: ip}, function(res){
  374. $("#f-area").val(res.data);
  375. });
  376. }
  377. getChatLog(data.id, 1);
  378. checkUser();
  379. }
  380. // 操作新连接用户的 dom操作
  381. function checkUser() {
  382. $(".layui-unselect").find('li').unbind("click"); // 防止事件叠加
  383. // 切换用户
  384. $(".layui-unselect").find('li').bind('click', function () {
  385. changeUserTab($(this));
  386. var uid = $(this).data('id');
  387. var avatar = $(this).data('avatar');
  388. var name = $(this).data('name');
  389. var ip = $(this).data('ip');
  390. // 展示相应的对话信息
  391. $('.chat-box ul').each(function () {
  392. if ('u-' + uid == $(this).attr('id')) {
  393. $(this).addClass('show-chat-detail').siblings().removeClass('show-chat-detail').attr('style', '');
  394. return false;
  395. }
  396. });
  397. // 去除消息提示
  398. $(this).find('span').eq(1).removeClass('layui-badge').text('');
  399. // 设置当前会话的用户
  400. $("#active-user").attr('data-id', uid).attr('data-name', name).attr('data-avatar', avatar).attr('data-ip', ip);
  401. // 右侧展示详情
  402. $("#f-user").val(name);
  403. $("#f-ip").val(ip);
  404. $.getJSON('/service/index/getCity', {ip: ip}, function(res){
  405. $("#f-area").val(res.data);
  406. });
  407. getChatLog(uid, 1);
  408. wordBottom();
  409. });
  410. }
  411. // 删除用户聊天面板
  412. function delUser(data) {
  413. $("#f-" + data.id).remove(); // 清除左侧的用户列表
  414. $('#u-' + data.id).remove(); // 清除右侧的聊天详情
  415. }
  416. // 发送快捷语句
  417. function sendWord(obj) {
  418. var msg = $(obj).data('word');
  419. sendMessage(msg);
  420. }
  421. // 获取聊天记录
  422. function getChatLog(uid, page, flag) {
  423. $.getJSON('/service/index/getChatLog', {uid: uid, page: page}, function(res){
  424. if(1 == res.code && res.data.length > 0){
  425. if(res.msg == res.total){
  426. var _html = '<div class="layui-flow-more">没有更多了</div>';
  427. }else{
  428. var _html = '<div class="layui-flow-more"><a href="javascript:;" data-page="' + parseInt(res.msg + 1)
  429. + '" onclick="getMore(this)"><cite>更多记录</cite></a></div>';
  430. }
  431. var len = res.data.length;
  432. for(var i = 0; i < len; i++){
  433. var v = res.data[len - i - 1];
  434. if ('mine' == v.type) {
  435. _html += '<li class="whisper-chat-mine">';
  436. } else {
  437. _html += '<li>';
  438. }
  439. _html += '<div class="whisper-chat-user">';
  440. _html += '<img src="' + v.from_avatar + '">';
  441. if ('mine' == v.type) {
  442. _html += '<cite><i>' + v.time_line + '</i>' + v.from_name + '</cite>';
  443. } else {
  444. _html += '<cite>' + v.from_name + '<i>' + v.time_line + '</i></cite>';
  445. }
  446. _html += '</div><div class="whisper-chat-text">' + replaceContent(v.content) + '</div>';
  447. _html += '</li>';
  448. }
  449. setTimeout(function () {
  450. // 滚动条自动定位到最底端
  451. if(typeof flag == 'undefined'){
  452. $("#u-" + uid).html(_html);
  453. wordBottom();
  454. }else{
  455. $("#u-" + uid).prepend(_html);
  456. }
  457. showBigPic();
  458. }, 100);
  459. }
  460. });
  461. }
  462. // 显示大图
  463. function showBigPic(){
  464. $(".layui-whisper-photos").on('click', function () {
  465. var src = this.src;
  466. layer.photos({
  467. photos: {
  468. data: [{
  469. "alt": "大图模式",
  470. "src": src
  471. }]
  472. }
  473. , shade: 0.5
  474. , closeBtn: 2
  475. , anim: 0
  476. , resize: false
  477. , success: function (layero, index) {
  478. }
  479. });
  480. });
  481. }
  482. // 获取更多的的记录
  483. function getMore(obj){
  484. $(obj).remove();
  485. var page = $(obj).attr('data-page');
  486. var uid = $(".layui-unselect").find('li').eq(0).data('id');
  487. getChatLog(uid, page, 1);
  488. }
  489. // 打卡下班
  490. function loginOut(){
  491. layer.msg("正在关闭,未咨询完的用户", {time: 50000});
  492. var len = $("#user_list li").length;
  493. var closeNum = 0;
  494. if(len == closeNum){
  495. window.location.href = '/service/login/loginOut';
  496. }
  497. $("#user_list li").each(function(){
  498. var uid = $(this).data('id');
  499. var activeUid = $("#active-user").attr('data-id');
  500. if(uid == activeUid){
  501. $("#active-user").attr('data-id', -999);
  502. }
  503. socket.send(JSON.stringify({
  504. type: 'closeUser', uid: uid
  505. }));
  506. $(this).parent().remove(); // 清除左侧的用户列表
  507. $('#u-' + uid).remove(); // 清除右侧的聊天详情
  508. closeNum++;
  509. if(closeNum == len){
  510. setTimeout(function(){
  511. window.location.href = '/service/login/loginOut';
  512. }, 1500); // 此处等待用户真的退出了
  513. }
  514. });
  515. }