chat-main.js 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617
  1. var app=new Vue({
  2. el: '#app',
  3. delimiters:["<{","}>"],
  4. data: {
  5. fullscreenLoading:true,
  6. leftTabActive:"first",
  7. rightTabActive:"visitorInfo",
  8. users:[],
  9. usersMap:[],
  10. //server:getWsBaseUrl()+"/ws_kefu?token="+localStorage.getItem("token"),
  11. server:getWsBaseUrl()+"/chat_server",
  12. socket:null,
  13. messageContent:"",
  14. currentGuest:"",
  15. msgList:[],
  16. chatTitle:"暂时未处理咨询",
  17. kfConfig:{
  18. id : "kf_1",
  19. name : "客服丽丽",
  20. avator : "",
  21. to_id : "",
  22. },
  23. visitor:{
  24. visitor_id:"",
  25. refer:"",
  26. client_ip:"",
  27. city:"",
  28. status:"",
  29. source_ip:"",
  30. created_at:"",
  31. },
  32. visitors:[],
  33. visitorCount:0,
  34. visitorCurrentPage:1,
  35. visitorPageSize:10,
  36. face:[
  37. "em-smile",
  38. "em-laughing",
  39. "em-blush",
  40. "em-smiley",
  41. "em-relaxed",
  42. "em-smirk",
  43. "em-heart_eyes",
  44. "em-kissing_heart",
  45. "em-kissing_closed_eyes",
  46. "em-flushed",
  47. "em-relieved",
  48. "em-satisfied",
  49. "em-grin",
  50. "em-wink",
  51. "em-stuck_out_tongue_winking_eye",
  52. "em-stuck_out_tongue_closed_eyes",
  53. "em-grinning",
  54. "em-kissing",
  55. "em-kissing_smiling_eyes",
  56. "em-stuck_out_tongue",
  57. "em-sleeping",
  58. "em-worried",
  59. "em-frowning",
  60. "em-anguished",
  61. "em-open_mouth",
  62. "em-grimacing",
  63. "em-confused",
  64. "em-hushed",
  65. "em-expressionless",
  66. "em-unamused",
  67. "em-sweat_smile",
  68. "em-sweat",
  69. "em-disappointed_relieved",
  70. "em-weary",
  71. "em-pensive",
  72. "em-disappointed",
  73. "em-confounded",
  74. "em-fearful",
  75. "em-cold_sweat",
  76. "em-persevere",
  77. "em-cry",
  78. "em-sob",
  79. "em-joy",
  80. "em-astonished",
  81. "em-scream",
  82. "em-tired_face",
  83. "em-angry",
  84. "em-rage",
  85. "em-triumph",
  86. "em-sleepy",
  87. "em-yum",
  88. "em-mask",
  89. "em-sunglasses",
  90. "em-dizzy_face",
  91. "em-imp",
  92. "em-smiling_imp",
  93. "em-neutral_face",
  94. "em-no_mouth",
  95. "em-innocent",
  96. "em-alien"
  97. ],
  98. },
  99. methods: {
  100. //跳转
  101. openUrl(url) {
  102. window.location.href = url;
  103. },
  104. sendKefuOnline(){
  105. let mes = {}
  106. mes.type = "kfOnline";
  107. mes.data = this.kfConfig;
  108. this.socket.send(JSON.stringify(mes));
  109. },
  110. //心跳
  111. ping(){
  112. let _this=this;
  113. let mes = {}
  114. mes.type = "ping";
  115. mes.data = "";
  116. setInterval(function () {
  117. if(_this.socket!=null){
  118. _this.socket.send(JSON.stringify(mes));
  119. }
  120. },5000)
  121. },
  122. //初始化websocket
  123. initConn() {
  124. let socket = new ReconnectingWebSocket(this.server);//创建Socket实例
  125. this.socket = socket
  126. this.socket.onmessage = this.OnMessage;
  127. this.socket.onopen = this.OnOpen;
  128. },
  129. OnOpen() {
  130. this.sendKefuOnline();
  131. },
  132. OnMessage(e) {
  133. const redata = JSON.parse(e.data);
  134. switch (redata.type){
  135. case "allUsers":
  136. this.handleOnlineUsers(redata.data);
  137. //this.sendKefuOnline();
  138. break;
  139. case "userOnline":
  140. this.addOnlineUser(redata.data);
  141. //发送通知
  142. let _this=this;
  143. notify(redata.data.username, {
  144. body: "上线了",
  145. icon: redata.data.avator
  146. }, function(notification) {
  147. //可直接打开通知notification相关联的tab窗口
  148. window.focus();
  149. $('#tab-first').trigger('click');
  150. notification.close();
  151. _this.talkTo(redata.data.uid,redata.data.username);
  152. });
  153. break;
  154. case "userOffline":
  155. this.removeOfflineUser(redata.data);
  156. //this.sendKefuOnline();
  157. break;
  158. case "notice":
  159. // if(!this.usersMap[redata.data.uid]){
  160. // this.$notify({
  161. // title: "通知",
  162. // message: "新客户访问",
  163. // type: 'success',
  164. // duration: 0,
  165. // });
  166. // }
  167. this.sendKefuOnline();
  168. break;
  169. }
  170. // if (redata.type == "notice") {
  171. // this.$notify({
  172. // title: "通知",
  173. // message: "新客户访问",
  174. // type: 'success',
  175. // duration: 0,
  176. // });
  177. //发送给客户我在线
  178. // let mes = {}
  179. // mes.type = "kfConnect";
  180. // kfConfig.guest_id=redata.data[0].uid;
  181. // mes.data = kfConfig;
  182. // this.socket.send(JSON.stringify(mes));
  183. //}
  184. if (redata.type == "message") {
  185. let msg = redata.data
  186. let content = {}
  187. let _this=this;
  188. content.avator = msg.avator;
  189. content.name = msg.name;
  190. content.content = replaceContent(msg.content);
  191. content.is_kefu = false;
  192. content.time = msg.time;
  193. if (msg.id == this.currentGuest) {
  194. this.msgList.push(content);
  195. }
  196. //发送通知
  197. notify(msg.name, {
  198. body: msg.content,
  199. icon: msg.avator
  200. }, function(notification) {
  201. //可直接打开通知notification相关联的tab窗口
  202. window.focus();
  203. notification.close();
  204. _this.talkTo(msg.id,msg.name);
  205. });
  206. for(let i=0;i<this.users.length;i++){
  207. if(this.users[i].uid==msg.id){
  208. this.$set(this.users[i],'last_message',msg.content);
  209. }
  210. }
  211. this.scrollBottom();
  212. }
  213. },
  214. //接手客户
  215. talkTo(guestId,name) {
  216. this.currentGuest = guestId;
  217. this.chatTitle=name+"|"+guestId+",正在处理中...";
  218. //发送给客户
  219. let mes = {}
  220. mes.type = "kfConnect";
  221. this.kfConfig.to_id=guestId;
  222. mes.data = this.kfConfig;
  223. this.socket.send(JSON.stringify(mes));
  224. //获取当前访客信息
  225. this.getVistorInfo(guestId);
  226. //获取当前客户消息
  227. this.getMesssagesByVisitorId(guestId);
  228. },
  229. //发送给客户
  230. chatToUser() {
  231. this.messageContent=this.messageContent.trim("\r\n");
  232. if(this.messageContent==""||this.messageContent=="\r\n"||this.currentGuest==""){
  233. return;
  234. }
  235. let _this=this;
  236. let mes = {};
  237. mes.type = "kefu";
  238. mes.content = this.messageContent;
  239. mes.from_id = this.kfConfig.id;
  240. mes.to_id = this.currentGuest;
  241. $.post("/message",mes,function(){
  242. _this.messageContent = "";
  243. });
  244. let content = {}
  245. content.avator = this.kfConfig.avator;
  246. content.name = this.kfConfig.name;
  247. content.content = replaceContent(this.messageContent);
  248. content.is_kefu = true;
  249. content.time = '';
  250. this.msgList.push(content);
  251. this.scrollBottom();
  252. },
  253. //处理当前在线用户列表
  254. addOnlineUser:function (retData) {
  255. var flag=false;
  256. retData.last_message="新访客";
  257. retData.status=1;
  258. retData.name=retData.username;
  259. for(let i=0;i<this.users.length;i++){
  260. if(this.users[i].uid==retData.uid){
  261. flag=true;
  262. }
  263. }
  264. if(!flag){
  265. this.users.unshift(retData);
  266. }
  267. for(let i=0;i<this.visitors.length;i++){
  268. if(this.visitors[i].visitor_id==retData.uid){
  269. this.visitors[i].status=1;
  270. break;
  271. }
  272. }
  273. },
  274. //处理当前在线用户列表
  275. removeOfflineUser:function (retData) {
  276. for(let i=0;i<this.users.length;i++){
  277. if(this.users[i].uid==retData.uid){
  278. this.users.splice(i,1);
  279. }
  280. }
  281. let vid=retData.uid;
  282. for(let i=0;i<this.visitors.length;i++){
  283. if(this.visitors[i].visitor_id==vid){
  284. this.visitors[i].status=0;
  285. break;
  286. }
  287. }
  288. },
  289. //处理当前在线用户列表
  290. handleOnlineUsers:function (retData) {
  291. if (this.currentGuest == "") {
  292. this.chatTitle = "连接成功,等待处理中...";
  293. }
  294. this.usersMap=[];
  295. for(let i=0;i<retData.length;i++){
  296. this.usersMap[retData[i].uid]=retData[i].username;
  297. retData[i].last_message="新访客";
  298. }
  299. if(this.users.length==0){
  300. this.users = retData;
  301. }
  302. for(let i=0;i<this.visitors.length;i++){
  303. let vid=this.visitors[i].visitor_id;
  304. if(typeof this.usersMap[vid]=="undefined"){
  305. this.visitors[i].status=0;
  306. }else{
  307. this.visitors[i].status=1;
  308. }
  309. }
  310. },
  311. //获取客服信息
  312. getKefuInfo(){
  313. let _this=this;
  314. $.ajax({
  315. type:"get",
  316. url:"/kefuinfo",
  317. headers:{
  318. "token":localStorage.getItem("token")
  319. },
  320. success: function(data) {
  321. if(data.code==200 && data.result!=null){
  322. _this.kfConfig.id=data.result.id;
  323. _this.kfConfig.name=data.result.name;
  324. _this.kfConfig.avator=data.result.avator;
  325. _this.initConn();
  326. }
  327. if(data.code!=200){
  328. _this.$message({
  329. message: data.msg,
  330. type: 'error'
  331. });
  332. }
  333. }
  334. });
  335. },
  336. //获取信息列表
  337. getMesssagesByVisitorId(visitorId){
  338. let _this=this;
  339. $.ajax({
  340. type:"get",
  341. url:"/messages?visitorId="+visitorId+"&kefuId="+_this.kfConfig.id,
  342. headers:{
  343. "token":localStorage.getItem("token")
  344. },
  345. success: function(data) {
  346. if(data.code==200 && data.result!=null){
  347. let msgList=data.result;
  348. _this.msgList=[];
  349. for(let i=0;i<msgList.length;i++){
  350. let visitorMes=msgList[i];
  351. let content = {}
  352. if(visitorMes["mes_type"]=="kefu"){
  353. content.is_kefu = true;
  354. content.avator = visitorMes["kefu_avator"];
  355. content.name = visitorMes["kefu_name"];
  356. }else{
  357. content.is_kefu = false;
  358. content.avator = visitorMes["visitor_avator"];
  359. content.name = visitorMes["visitor_name"];
  360. }
  361. content.content = replaceContent(visitorMes["content"]);
  362. content.time = visitorMes["time"];
  363. _this.msgList.push(content);
  364. _this.scrollBottom();
  365. }
  366. }
  367. if(data.code!=200){
  368. _this.$message({
  369. message: data.msg,
  370. type: 'error'
  371. });
  372. }
  373. }
  374. });
  375. },
  376. //获取客服信息
  377. getVistorInfo(vid){
  378. let _this=this;
  379. $.ajax({
  380. type:"get",
  381. url:"/visitor",
  382. data:{visitorId:vid},
  383. headers:{
  384. "token":localStorage.getItem("token")
  385. },
  386. success: function(data) {
  387. if(data.result!=null){
  388. let r=data.result;
  389. _this.visitor.created_at=r.created_at;
  390. _this.visitor.refer=r.refer;
  391. _this.visitor.city=r.city;
  392. _this.visitor.client_ip=r.client_ip;
  393. _this.visitor.source_ip=r.source_ip;
  394. _this.visitor.status=r.status==1?"在线":"离线";
  395. _this.visitor.visitor_id=r.visitor_id;
  396. }
  397. if(data.code!=200){
  398. _this.$message({
  399. message: data.msg,
  400. type: 'error'
  401. });
  402. }
  403. }
  404. });
  405. },
  406. //关闭访客
  407. closeVisitor(visitorId){
  408. let _this=this;
  409. $.ajax({
  410. type:"get",
  411. url:"/message_close",
  412. data:{visitor_id:visitorId},
  413. headers:{
  414. "token":localStorage.getItem("token")
  415. },
  416. success: function(data) {
  417. if(data.code!=200){
  418. _this.$message({
  419. message: data.msg,
  420. type: 'error'
  421. });
  422. }
  423. }
  424. });
  425. },
  426. //处理tab切换
  427. handleTabClick(tab, event){
  428. let _this=this;
  429. if(tab.name=="second"){
  430. this.getVisitorPage(1);
  431. }
  432. if(tab.name=="blackList"){
  433. }
  434. },
  435. //所有访客分页展示
  436. visitorPage(page){
  437. this.getVisitorPage(page);
  438. },
  439. //获取访客分页
  440. getVisitorPage(page){
  441. let _this=this;
  442. $.ajax({
  443. type:"get",
  444. url:"/visitors",
  445. data:{page:page},
  446. headers:{
  447. "token":localStorage.getItem("token")
  448. },
  449. success: function(data) {
  450. if(data.result.list!=null){
  451. _this.visitors=data.result.list;
  452. _this.visitorCount=data.result.count;
  453. _this.visitorPageSize=data.result.pagesize;
  454. }
  455. if(data.code!=200){
  456. _this.$message({
  457. message: data.msg,
  458. type: 'error'
  459. });
  460. }
  461. }
  462. });
  463. },
  464. //滚到底部
  465. scrollBottom(){
  466. this.$nextTick(() => {
  467. $('.chatBox').scrollTop($(".chatBox")[0].scrollHeight);
  468. });
  469. },
  470. //jquery
  471. initJquery(){
  472. this.$nextTick(() => {
  473. var _this=this;
  474. $(function () {
  475. //展示表情
  476. var faces=placeFace();
  477. $.each(faceTitles, function (index, item) {
  478. // _this.face.push({"name":item,"path":faces[item]});
  479. });
  480. $(".faceBtn").click(function(){
  481. var status=$('.faceBox').css("display");
  482. if(status=="block"){
  483. $('.faceBox').hide();
  484. }else{
  485. $('.faceBox').show();
  486. }
  487. });
  488. });
  489. });
  490. },
  491. //表情点击事件
  492. faceIconClick(index){
  493. $('.faceBox').hide();
  494. this.messageContent+="face["+this.face[index]+"]";
  495. },
  496. //上传图片
  497. uploadImg (url){
  498. let _this=this;
  499. $('#uploadImg').after('<input type="file" accept="image/gif,image/jpeg,image/jpg,image/png" id="uploadImgFile" name="file" style="display:none" >');
  500. $("#uploadImgFile").click();
  501. $("#uploadImgFile").change(function (e) {
  502. var formData = new FormData();
  503. var file = $("#uploadImgFile")[0].files[0];
  504. formData.append("imgfile",file); //传给后台的file的key值是可以自己定义的
  505. filter(file) && $.ajax({
  506. url: url || '',
  507. type: "post",
  508. data: formData,
  509. contentType: false,
  510. processData: false,
  511. dataType: 'JSON',
  512. mimeType: "multipart/form-data",
  513. success: function (res) {
  514. if(res.code!=200){
  515. _this.$message({
  516. message: res.msg,
  517. type: 'error'
  518. });
  519. }else{
  520. _this.messageContent+='img[' + res.result.path + ']';
  521. _this.chatToUser();
  522. }
  523. },
  524. error: function (data) {
  525. console.log(data);
  526. }
  527. });
  528. });
  529. },
  530. addIpblack(ip){
  531. let _this=this;
  532. $.ajax({
  533. type:"post",
  534. url:"/ipblack",
  535. data:{ip:ip},
  536. headers:{
  537. "token":localStorage.getItem("token")
  538. },
  539. success: function(data) {
  540. if(data.code!=200){
  541. _this.$message({
  542. message: data.msg,
  543. type: 'error'
  544. });
  545. }else{
  546. _this.$message({
  547. message: data.msg,
  548. type: 'success'
  549. });
  550. }
  551. }
  552. });
  553. },
  554. //粘贴上传图片
  555. onPasteUpload(event){
  556. let items = event.clipboardData && event.clipboardData.items;
  557. let file = null
  558. if (items && items.length) {
  559. // 检索剪切板items
  560. for (var i = 0; i < items.length; i++) {
  561. if (items[i].type.indexOf('image') !== -1) {
  562. file = items[i].getAsFile()
  563. }
  564. }
  565. }
  566. if (!file) {
  567. return;
  568. }
  569. let _this=this;
  570. var formData = new FormData();
  571. formData.append('imgfile', file);
  572. $.ajax({
  573. url: '/uploadimg',
  574. type: "post",
  575. data: formData,
  576. contentType: false,
  577. processData: false,
  578. dataType: 'JSON',
  579. mimeType: "multipart/form-data",
  580. success: function (res) {
  581. if(res.code!=200){
  582. _this.$message({
  583. message: res.msg,
  584. type: 'error'
  585. });
  586. }else{
  587. _this.messageContent+='img[' + res.result.path + ']';
  588. _this.chatToUser();
  589. }
  590. },
  591. error: function (data) {
  592. console.log(data);
  593. }
  594. });
  595. },
  596. openUrl(url){
  597. window.open(url);
  598. },
  599. },
  600. mounted() {
  601. document.addEventListener('paste', this.onPasteUpload)
  602. },
  603. created: function () {
  604. //jquery
  605. this.initJquery();
  606. this.getKefuInfo();
  607. //心跳
  608. this.ping();
  609. }
  610. })