whisper.js 18 KB

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