ccPhoneBarSocket.js 51 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840
  1. /*
  2. * 呼叫中心电话工具条
  3. * author: easycallcenter365@126.com
  4. * 2025-04-08
  5. */
  6. function _phoneBarObserver(){
  7. if(!(this instanceof _phoneBarObserver)){
  8. return new _phoneBarObserver();
  9. }
  10. this.listeners = {}
  11. };
  12. _phoneBarObserver.prototype = {
  13. on: function (key, callback) { this.addListener(key, callback); },
  14. off: function (key, callback) { this.removeListener(key, callback); },
  15. addListener: function (key, callback) {
  16. if(!this.listeners[key]) {
  17. this.listeners[key] = [];
  18. }
  19. this.listeners[key].push(callback);
  20. },
  21. removeListener: function (key, callback) {
  22. if(this.listeners[key]) {
  23. if(callback) {
  24. for (var i = 0; i < this.listeners[key].length; i++) {
  25. if (this.listeners[key][i] === callback) {
  26. delete this.listeners[key][i];
  27. }
  28. }
  29. }else {
  30. this.listeners[key] = [];
  31. }
  32. }
  33. },
  34. notifyAll: function (key, info) {
  35. if(!this.listeners[key]) return;
  36. for (var i = 0; i < this.listeners[key].length; i++) {
  37. if (typeof this.listeners[key][i] === 'function') {
  38. this.listeners[key][i](info);
  39. }
  40. }
  41. },
  42. make: function (o) {
  43. for (var i in this) {
  44. o[i] = this[i];
  45. o.listeners = {}
  46. }
  47. }
  48. };
  49. //呼叫中心websocket通信对象
  50. "use strict";
  51. function ccPhoneBarSocket() {
  52. var observer = new _phoneBarObserver();
  53. observer.make(this);
  54. var _cc = this;
  55. var ws = null;
  56. var wsuri = null;
  57. var isConnected = false;
  58. /* 通话已建立 */
  59. var callConnected = false;
  60. /*
  61. * 是否可以发送视频通话邀请;
  62. */
  63. var canSendVideoReInvite = false;
  64. /**
  65. * 是否开启了坐席状态列表订阅
  66. * @type {boolean}
  67. */
  68. this.subscribeAgentListStarted = false;
  69. this.iframe = null;
  70. this.callConfig = {
  71. // 使用默认的UI,还是使用自定义的UI
  72. 'useDefaultUi': false,
  73. //呼叫控制服务器地址
  74. 'ipccServer': '127.0.0.1:8443',
  75. //是否启用websocket安全连接
  76. 'enableWss' : false,
  77. //语音编码
  78. 'callCodec' : 'pcma',
  79. //是否发送心跳数据
  80. 'enableHeartBeat' : true,
  81. //送心跳数据的时间间隔; 秒;
  82. 'heartBeatIntervalSecs' : 16,
  83. // 工具条外呼时: 软电话和外呼通话,使用相同的语音编码,避免转码,从而提高性能;
  84. // 但是!: 如果客户端是网页电话,且外呼线路使用g729编码时,该参数需要设置为false,
  85. // 因为网页电话不支持g729编码; [此时会产生语音编码的转码]
  86. 'useSameAudioCodeForOutbound' : true,
  87. // 令牌
  88. 'loginToken' : '',
  89. // 网关列表, 如果是注册模式: 网关地址参数则填写为网关名称;
  90. // 安全起见,生产环境,需要把该参数加密为base64格式;
  91. 'gatewayList' : [
  92. {
  93. uuid : '01',
  94. updateTime : 1675953810492,
  95. gatewayAddr : '192.168.3.111:5080;external', // 网关地址 + 外呼环境[可选参数,默认为external];
  96. callerNumber : '007', // 主叫号码
  97. calleePrefix : '', // 被叫前缀
  98. priority : 1, // 优先级
  99. concurrency : 10, // 并发数
  100. register : false, // 是否注册模式
  101. audioCodec : 'g711' // 语音编码,可用选择项 g711、g729
  102. }
  103. ],
  104. 'gatewayEncrypted' : false,
  105. // 分机注册配置;
  106. //Freeswitch服务器地址
  107. 'fsServer' : '192.168.3.111:5060',
  108. // 分机账户
  109. 'extnum' : '',
  110. // 工号
  111. 'opnum' : '',
  112. //业务组编号
  113. 'groupId' : '',
  114. //全部业务组列表
  115. 'groups' : null,
  116. //全部坐席人员列表
  117. 'agentList' : null,
  118. 'extPassword': 'zfAn1l2mjx86lyX9U33xNf%2FKx15dOf6ucnDDK9nfnkA%3D', // 分机密码
  119. 'phoneType': 'EyeBeam', // 电话类型
  120. 'webPhoneUrl' : 'None',
  121. // 客户端软电话代理配置信息
  122. 'localHostProxyVersion' : 'v20221130_1736', // 本地代理软件的版本信息
  123. 'localHostProxyPort' : 8888 // 本地代理软件端口;
  124. };
  125. /**
  126. * 在指定html对象后追加新元素
  127. * @param newElement
  128. * @param targetElement
  129. */
  130. this.insertAfter = function(newElement,targetElement)
  131. {
  132. var parent = targetElement.parentNode;
  133. if(parent.lastChild === targetElement)
  134. {
  135. parent.appendChild(newElement);
  136. }else{
  137. parent.insertBefore(newElement,targetElement.nextSibling);
  138. }
  139. };
  140. this.trim = function (str) {
  141. return str.replace(/^\s\s*/,'').replace(/\s\s*$/, '');
  142. };
  143. this.getIsConnected = function(){
  144. return isConnected;
  145. };
  146. /**
  147. * 获取通话状态
  148. * @returns {boolean} true通话中,false通话未建立;
  149. */
  150. this.getCallConnected = function(){
  151. return callConnected;
  152. };
  153. /**
  154. * 设置通话状态;
  155. * @param value true通话中,false通话未建立
  156. * @returns {*}
  157. */
  158. this.setCallConnected = function(value){
  159. callConnected = value;
  160. };
  161. /**
  162. * 设置一个标志,指示是否可以发起视频通话;
  163. * 仅限通话为音频通话且通话已经接通时方可允许发起视频邀请;
  164. * @param value
  165. */
  166. this.setCanSendVideoReInvite = function(value){
  167. canSendVideoReInvite = value;
  168. };
  169. /**
  170. * 设置一个标志,指示是否可以发起视频通话;
  171. * @param value
  172. */
  173. this.getCanSendVideoReInvite = function(){
  174. return canSendVideoReInvite;
  175. };
  176. this.setHeartbeat = function()
  177. {
  178. setInterval(function(){
  179. //如果启用了心跳,而且用户已经登录上线,则发送心跳数据
  180. if(_cc.callConfig.enableHeartBeat && _cc.getIsConnected()){
  181. console.debug("try to send heartbeat.");
  182. var heartBeat = {};
  183. heartBeat.action="setHearBeat";
  184. heartBeat.body = "{}";
  185. _cc.sendMsg(heartBeat);
  186. }
  187. }, _cc.callConfig.heartBeatIntervalSecs * 1000);
  188. };
  189. this.loadScript = function(destUrl, callbackFunc){
  190. var script = document.createElement("script");
  191. script.type = "text/javascript";
  192. script.src = destUrl;
  193. if(null != callbackFunc){
  194. script.onload=function(){callbackFunc();}
  195. }
  196. document.getElementsByTagName('head')[0].appendChild(script);
  197. };
  198. // 初始化websocket连接参数
  199. this.initConfig = function(config) {
  200. //把config中的属性全部拷贝到callConfig中;
  201. for(var element in config) {
  202. this.callConfig[element] = config[element];
  203. }
  204. var _loginToken = this.callConfig["loginToken"];
  205. if(typeof(_loginToken) == "undefined" && _loginToken === "") {
  206. alert("电话工具条:无法获取 loginToken!");
  207. return;
  208. }
  209. console.log("callConfig:", this.callConfig);
  210. wsuri = 'ws://' + this.callConfig.ipccServer +
  211. '/call-center/websocketServer?' +
  212. '&loginToken=' + this.callConfig.loginToken;
  213. console.log("ipccServer ws url: " + wsuri);
  214. var ipccServerIpAddr = this.callConfig.ipccServer.split(":");
  215. if(this.callConfig.enableWss && this.checkIP(ipccServerIpAddr)){
  216. var tipsError = "ERROR! 启用了wss之后,必须使用域名访问websocketServer! " + this.callConfig.ipccServer;
  217. console.log(tipsError);
  218. alert(tipsError);
  219. }
  220. if(this.callConfig.enableHeartBeat) {
  221. _cc.setHeartbeat();
  222. }
  223. //var downLoadUrl = "http://192.168.66.71:81/soft/callcenter-soft/LocalHostProxy/LocalHostProxy-" +
  224. _cc.callConfig["localHostProxyVersion"] + ".zip";
  225. //console.log("客户端代理软件的下载地址:", downLoadUrl);
  226. //this.createIframe("http://127.0.0.1:8888/getVersion");
  227. if(_cc.callConfig["useDefaultUi"]){
  228. this.initPhoneBarUI();
  229. }
  230. };
  231. //检测ip地址是否合法;
  232. this.checkIP = function (ip)
  233. {
  234. var re = /^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])$/ ;
  235. return re.test(ip);
  236. };
  237. //断开到呼叫控制服务器的连接
  238. this.disconnect = function(){
  239. var cmdInfo = {};
  240. cmdInfo.action="setAgentStatus";
  241. cmdInfo.body = {"cmd" : "disconnect", "args" : { "msg" : "disconnection opt triggered by js client." } };
  242. ws.send(JSON.stringify(cmdInfo));
  243. };
  244. /**
  245. * 获取当前坐席登录的分机号码
  246. * @returns {string|*}
  247. */
  248. this.getExtNum = function(){
  249. return this.callConfig.extnum;
  250. };
  251. /**
  252. * 获取当前坐席登录的工号
  253. * @returns {string|*}
  254. */
  255. this.getOpNum = function(){
  256. return this.callConfig.opnum;
  257. };
  258. /**
  259. * 获取当前坐席的业务组编号
  260. * @returns {string|*}
  261. */
  262. this.getGroupId = function(){
  263. return this.callConfig.groupId;
  264. };
  265. /**
  266. * 获取全部业务组列表
  267. * @returns {string|*}
  268. */
  269. this.getGroups = function(){
  270. return JSON.parse(JSON.stringify(this.callConfig.groups));
  271. };
  272. this.findAgentByOpNum = function (opnum){
  273. if(_cc.callConfig.agentList == null) return null;
  274. for (var key in _cc.callConfig.agentList) {
  275. let item = _cc.callConfig.agentList[key];
  276. if(item["opnum"] === opnum){
  277. //add _arrayIndex property to record index
  278. item["_arrayIndex"] = key;
  279. return item;
  280. }
  281. }
  282. return null;
  283. };
  284. this.updateAgentList = function (agentList) {
  285. for (var key in agentList) {
  286. let item = agentList[key];
  287. var existItem = this.findAgentByOpNum(item["opnum"]);
  288. if(existItem != null){
  289. var newStatus = item["agentStatus"];
  290. var oldStatus = existItem["agentStatus"];
  291. var logoutTime = item["logoutTime"];
  292. if(logoutTime > 0){
  293. //删除元素
  294. console.info("delete offline user",item["opnum"], "index=", key);
  295. _cc.callConfig.agentList.splice(existItem["_arrayIndex"], 1);
  296. continue;
  297. }
  298. if(newStatus != oldStatus){
  299. existItem["agentStatus"] = newStatus;
  300. }
  301. }else{
  302. if(_cc.callConfig.agentList != null) {
  303. _cc.callConfig.agentList[_cc.callConfig.agentList.length + 1] = item;
  304. }
  305. }
  306. }
  307. };
  308. //连接到呼叫控制服务器
  309. this.connect = function() {
  310. if ('WebSocket' in window)
  311. ws = new WebSocket(wsuri);
  312. else {
  313. console.log('您的浏览器不支持websocket,您无法使用本页面的功能!');
  314. return;
  315. }
  316. //收到消息
  317. ws.onmessage = function(evt) {
  318. console.log("recv msg from websocket server: ", evt.data);
  319. var msg = JSON.parse(evt.data);
  320. console.log("parsed json data:", msg);
  321. var resp_status = msg["status"];
  322. switch(resp_status) {
  323. case 200:
  324. isConnected = true;
  325. _cc.callConfig.extnum = msg.object["extnum"];
  326. _cc.callConfig.opnum = msg.object["opnum"];
  327. _cc.callConfig.groupId = msg.object["groupId"];
  328. _cc.callConfig.groups = msg.object["groups"];
  329. console.log('ipcc连接成功.', 'connected_ipcc_server');
  330. _cc.notifyAll(ccPhoneBarSocket.eventList.ws_connected, msg);
  331. _cc.setStatus(ccPhoneBarSocket.agentStatusEnum.busy);
  332. break;
  333. default:
  334. if (parseInt(resp_status) === parseInt(ccPhoneBarSocket.eventList.caller_hangup) ||
  335. parseInt(resp_status) === parseInt(ccPhoneBarSocket.eventList.CONFERENCE_MODERATOR_HANGUP)) {
  336. _cc.setCallConnected(false);
  337. _cc.setCanSendVideoReInvite(false);
  338. _cc.callConfig.agentList = null;
  339. _cc.unSubscribeAgentList();
  340. console.log("caller_hangup")
  341. }
  342. if (parseInt(resp_status) === parseInt(ccPhoneBarSocket.eventList.callee_answered)) {
  343. _cc.setCallConnected(true);
  344. console.log("callee_answered")
  345. }
  346. if (parseInt(resp_status) === parseInt(ccPhoneBarSocket.eventList.new_inbound_call)) {
  347. _cc.setCallConnected(true);
  348. console.log("new_inbound_call")
  349. }
  350. if (parseInt(resp_status) === parseInt(ccPhoneBarSocket.eventList.caller_answered) ||
  351. parseInt(resp_status) === parseInt(ccPhoneBarSocket.eventList.callee_answered)) {
  352. if (msg["object"]["callType"] === "audio") {
  353. _cc.setCanSendVideoReInvite(true);
  354. _cc.notifyAll(ccPhoneBarSocket.eventList.on_audio_call_connected, "audio call connected.")
  355. console.log("current callType is audio.")
  356. }
  357. if (msg["object"]["callType"] === "video") {
  358. _cc.setCanSendVideoReInvite(false);
  359. _cc.notifyAll(ccPhoneBarSocket.eventList.on_video_call_connected, "video call connected.")
  360. console.log("current callType is video.")
  361. }
  362. }
  363. if (parseInt(resp_status) === parseInt(ccPhoneBarSocket.eventList.customer_channel_hold)) {
  364. _cc.changeUiOnHold();
  365. }
  366. if (parseInt(resp_status) === parseInt(ccPhoneBarSocket.eventList.customer_channel_unhold)) {
  367. _cc.changeUiOnUnHold();
  368. }
  369. if (parseInt(resp_status) === parseInt(ccPhoneBarSocket.eventList.customer_on_hold_hangup)) {
  370. _cc.changeUiOnUnHold();
  371. $("#holdBtn").removeClass('on');
  372. $("#callStatus").text("保持的通话已挂机.");
  373. }
  374. if (parseInt(resp_status) === parseInt(ccPhoneBarSocket.eventList.customer_channel_call_wait)) {
  375. $("#stopCallWait").show();
  376. $("#doConsultationBtn").hide();
  377. $("#callStatus").text("客户电话等待中.");
  378. _cc.showTransferAreaUI();
  379. }
  380. if (parseInt(resp_status) === parseInt(ccPhoneBarSocket.eventList.customer_channel_off_call_wait)) {
  381. $("#stopCallWait").hide();
  382. $("#transferCallWait").hide();
  383. _cc.hideTransferAreaUI();
  384. $("#callStatus").text("等待的电话已接回.");
  385. }
  386. if (parseInt(resp_status) === parseInt(ccPhoneBarSocket.eventList.inner_consultation_start)) {
  387. $("#callStatus").text("咨询已开始.");
  388. $("#transferCallWait").show();
  389. }
  390. if (parseInt(resp_status) === parseInt(ccPhoneBarSocket.eventList.inner_consultation_stop)) {
  391. $("#callStatus").text("咨询已结束.");
  392. $("#transferCallWait").hide();
  393. }
  394. if (parseInt(resp_status) === parseInt(ccPhoneBarSocket.eventList.customer_on_call_wait_hangup)) {
  395. $("#stopCallWait").hide();
  396. $("#transferCallWait").hide();
  397. $("#callStatus").text("等待的客户已挂机.");
  398. }
  399. if (parseInt(resp_status) === parseInt(ccPhoneBarSocket.eventList.inner_consultation_start)) {
  400. $("#holdBtn").removeClass('on');
  401. $("#hangUpBtn").removeClass('on');
  402. $("#transferBtn").removeClass('on');
  403. }
  404. if (parseInt(resp_status) === parseInt(ccPhoneBarSocket.eventList.transfer_call_success)) {
  405. var extNum = msg["object"]["callee"];
  406. if(extNum === _cc.getExtNum()) {
  407. $("#holdBtn").addClass('on');
  408. $("#hangUpBtn").addClass('on');
  409. $("#transferBtn").addClass('on');
  410. }
  411. }
  412. if (parseInt(resp_status) === parseInt(ccPhoneBarSocket.eventList.agent_status_data_changed)) {
  413. if(_cc.subscribeAgentListStarted) {
  414. if (_cc.callConfig.agentList == null) {
  415. _cc.callConfig.agentList = JSON.parse(msg.object);
  416. }else{
  417. // 更新列表;
  418. _cc.updateAgentList(JSON.parse(msg.object));
  419. }
  420. }
  421. }
  422. _cc.notifyAll(resp_status, msg);
  423. break;
  424. }
  425. };
  426. //关闭连接时触发
  427. ws.onclose = function(evt) {
  428. isConnected = false;
  429. _cc.notifyAll(ccPhoneBarSocket.eventList.ws_disconnected, "ipccserver 连接断开.");
  430. console.log("ipcc连接断开.", "disconnected");
  431. console.log(evt);
  432. ws.close();
  433. };
  434. ws.onopen = function(evt) {
  435. console.log("ipccserver websocket onopen...");
  436. };
  437. };
  438. //发送消息给呼叫控制服务器
  439. this.sendMsg = function(jsonObject) {
  440. console.debug("ws.send:", jsonObject);
  441. ws.send(JSON.stringify(jsonObject));
  442. };
  443. this.changeUiOnHold = function() {
  444. $("#holdBtnLi").hide();
  445. $("#unHoldBtnLi").show();
  446. $("#unHoldBtn").addClass('on');
  447. };
  448. this.changeUiOnUnHold = function() {
  449. $("#holdBtnLi").show();
  450. $("#holdBtn").addClass('on');
  451. $("#unHoldBtnLi").hide();
  452. };
  453. this.hideTransferAreaUI = function(){
  454. var transferArea = document.getElementById("transfer_area");
  455. transferArea.style.display = "none";
  456. };
  457. this.showTransferAreaUI = function(){
  458. var transferArea = document.getElementById("transfer_area");
  459. transferArea.style.display = "block";
  460. };
  461. /**
  462. * 设置状态为空闲
  463. */
  464. this.setStatusFree = function(){
  465. this.setStatus(ccPhoneBarSocket.agentStatusEnum.free);
  466. };
  467. /**
  468. * 设置状态为忙碌
  469. */
  470. this.setStatusBusy = function(){
  471. this.setStatus(ccPhoneBarSocket.agentStatusEnum.busy);
  472. };
  473. ccPhoneBarSocket.utils = {
  474. /**
  475. * 读取 URL 中的指定查询参数值
  476. * @param {string} paramName - 要获取的参数名称
  477. * @returns {string|null} 返回参数值,如果不存在则返回 null
  478. */
  479. getQueryParam : function (paramName) {
  480. const url = window.location.href; // 获取当前页面 URL
  481. const params = new URLSearchParams(new URL(url).search); // 创建 URLSearchParams 对象
  482. return params.get(paramName); // 获取指定参数值
  483. }
  484. };
  485. /**
  486. * 座席状态枚举;
  487. * @type {{rest: number, calling: number, busy: number, free: number, justLogin: number, meeting: number, train: number}}
  488. */
  489. ccPhoneBarSocket.agentStatusEnum = {
  490. /**
  491. * 刚刚上线,尚未就绪中;
  492. */
  493. "justLogin" : 1,
  494. /**
  495. * 空闲
  496. */
  497. "free" : 2,
  498. /**
  499. * 忙碌
  500. */
  501. "busy" : 3,
  502. /**
  503. * 小休
  504. */
  505. "busy_rest" : 31,
  506. /**
  507. * 线下会议
  508. */
  509. "busy_meeting" : 32,
  510. /**
  511. * 培训
  512. */
  513. "busy_training" : 33,
  514. /**
  515. * 通话中
  516. */
  517. "incall" : 4,
  518. /**
  519. * 话后处理,填写表单中
  520. */
  521. "fill_form" : 5,
  522. /**
  523. * 电话会议中
  524. */
  525. "conference" : 6
  526. };
  527. //定义视频level-id
  528. ccPhoneBarSocket.videoLevels = {
  529. "Smooth" : { "levelId" : "42e00b", "description" : "流畅" },
  530. "Smooth2" : { "levelId" : "42e00c", "description" : "流畅+" },
  531. "Smooth3" : { "levelId" : "42e00d", "description" : "流畅++" },
  532. "Clear" : { "levelId" : "42e014", "description" : "清晰" },
  533. "Clear2" : { "levelId" : "42e015", "description" : "清晰+" },
  534. "Clear3" : { "levelId" : "42e016", "description" : "清晰++" },
  535. "HD" : { "levelId" : "42e01e", "description" : "高清" },
  536. "HD2" : { "levelId" : "42e01f", "description" : "高清+" }
  537. };
  538. /**
  539. * 系统事件列表;
  540. * @type {{}}
  541. */
  542. ccPhoneBarSocket.eventList = {
  543. // 当音频通话已建立
  544. "on_audio_call_connected" : "100",
  545. "on_video_call_connected" : "101",
  546. // websocketServer连接成功
  547. "ws_connected": "200",
  548. // 当前用户已在其他设备登录;
  549. "user_login_on_other_device" : "201",
  550. // 用户下线
  551. "ws_disconnected" : "202",
  552. // 通话状态发生改变 [监听的数据]
  553. "call_session_status_data_changed" : "203",
  554. // 坐席状态列表发生改变
  555. "agent_status_data_changed" : "205",
  556. //请求参数错误
  557. "request_args_error" : "400",
  558. // unauthorized
  559. "unauthorized" : "401",
  560. //服务器内部错误
  561. "server_error" : "500",
  562. // 语音编码不匹配
  563. "server_error_audio_codec_not_match" : "501",
  564. // 主叫接通
  565. "caller_answered" : "600",
  566. // 主叫挂断
  567. "caller_hangup" : "601",
  568. // 主叫忙; 上一通电话未挂机
  569. "caller_busy" : "602",
  570. //主叫未登录
  571. "caller_not_login" : "603",
  572. //主叫应答超时
  573. "caller_respond_timeout" : "604",
  574. // 被叫接通
  575. "callee_answered" : "605",
  576. // 被叫挂断
  577. "callee_hangup" : "606",
  578. //被叫振铃
  579. "callee_ringing" : "607",
  580. // 座席状态改变
  581. "status_changed" : "608",
  582. // 一个完整的外呼任务结束: [可能尝试了一个或多个网关]
  583. "outbound_finished" : "611",
  584. // 预测外呼,分配的来电;
  585. "PREDICTIVE_CALL_INBOUND" : "612",
  586. // ACD队列分配的新来电
  587. "new_inbound_call" : "613",
  588. // 当前业务组实时排队人数
  589. "acd_group_queue_number" : "615",
  590. /**
  591. * 收到转接的来电请求
  592. */
  593. "transfer_call_recv" : 616,
  594. /**
  595. * 锁定坐席失败
  596. */
  597. "lock_agent_fail" : 617,
  598. /**
  599. * 通话已经转接成功
  600. */
  601. "transfer_call_success" : 618,
  602. /**
  603. * 产生asr语音识别结果
  604. */
  605. "asr_result_generate" : 619,
  606. /**
  607. * ASR语音识别流程结束(坐席侧)
  608. */
  609. "asr_process_end_agent" : 620,
  610. /**
  611. * ASR语音识别流程结束(客户侧)
  612. */
  613. "asr_process_end_customer" : 621,
  614. "asr_process_started" : 622,
  615. /**
  616. * customer call session hold.
  617. */
  618. "customer_channel_hold" : 623,
  619. /**
  620. * customer call session unHold.
  621. */
  622. "customer_channel_unhold" : 624,
  623. /**
  624. * customer call session on hold is hangup.
  625. */
  626. "customer_on_hold_hangup" : 625,
  627. "inner_consultation_request" : 626,
  628. /**
  629. * customer call session on call-wait.
  630. */
  631. "customer_channel_call_wait" : 627,
  632. /**
  633. * customer call session off call-wait.
  634. */
  635. "customer_channel_off_call_wait" : 628,
  636. /**
  637. * customer call session on call-wait is hangup.
  638. */
  639. "customer_on_call_wait_hangup" : 629,
  640. /**
  641. * extension on line event
  642. */
  643. "extension_on_line" : 630,
  644. /**
  645. * extension off line event
  646. */
  647. "extension_off_line" : 631,
  648. /**
  649. * Notify the agent that the call consultation has started.
  650. */
  651. "inner_consultation_start" : 632,
  652. /**
  653. * Notify the agent that the call consultation has stopped.
  654. */
  655. "inner_consultation_stop" : 633,
  656. /**
  657. * 多人电话会议,重复的被叫 ,
  658. */
  659. "conference_repeat_callee" : 660 ,
  660. /**
  661. * 多人电话会议,呼叫成员超时 ,
  662. */
  663. "CONFERENCE_CALL_MODERATOR_TIMEOUT" : 661 ,
  664. /**
  665. * 多人电话会议,成员接通 ,
  666. */
  667. "CONFERENCE_MEMBER_ANSWERED" : 662 ,
  668. /**
  669. * 多人电话会议,成员挂机 ,
  670. */
  671. "CONFERENCE_MEMBER_HANGUP" : 663 ,
  672. /**
  673. * 多人电话会议,成员禁言成功 ,
  674. */
  675. "CONFERENCE_MEMBER_MUTED_SUCCESS" : 666 ,
  676. /**
  677. * 多人电话会议,成员禁言失败 ,
  678. */
  679. "CONFERENCE_MEMBER_MUTED_FAILED" : 665 ,
  680. /**
  681. * 多人电话会议,成员解除禁言成功 ,
  682. */
  683. "CONFERENCE_MEMBER_UNMUTED_SUCCESS" : 667 ,
  684. /**
  685. * 多人电话会议,成员解除禁言失败 ,
  686. */
  687. "CONFERENCE_MEMBER_UNMUTED_FAILED" : 668 ,
  688. /**
  689. * 多人电话会议,会议成员不存在,无法执行相关操作:
  690. */
  691. "CONFERENCE_MEMBER_NOT_EXISTS" : "669" ,
  692. /**
  693. * 多人电话会议,主持人重置会议 ,
  694. */
  695. "CONFERENCE_MODERATOR_RESET" : "670" ,
  696. /**
  697. * 多人电话会议,主持人接通 ,
  698. */
  699. "CONFERENCE_MODERATOR_ANSWERED" : "671" ,
  700. /**
  701. * 多人电话会议,主持人挂机,会议结束 ,
  702. */
  703. "CONFERENCE_MODERATOR_HANGUP" : "672",
  704. /*
  705. * 成员视频禁用成功
  706. */
  707. "CONFERENCE_MEMBER_VMUTED_SUCCESS" : "674",
  708. /*
  709. * 成员解除视频禁用成功
  710. */
  711. "CONFERENCE_MEMBER_UnVMUTED_SUCCESS" : "676",
  712. /**
  713. * 多人电话会议,成员解除视频禁用失败;
  714. */
  715. "CONFERENCE_MEMBER_UnVMUTED_FAILED" : "677",
  716. /**
  717. * 成功把通话转接到多人视频会议
  718. */
  719. "CONFERENCE_TRANSFER_SUCCESS_FROM_EXISTED_CALL" : "678",
  720. /**
  721. * outbound start event
  722. */
  723. "OUTBOUND_START" : "679"
  724. };
  725. this.createIframe = function(src){
  726. var _iframe = document.createElement("iframe");
  727. _iframe.style.width = '0';
  728. _iframe.style.height = '0';
  729. _iframe.style.margin = '0';
  730. _iframe.style.padding = '0';
  731. _iframe.style.overflow = 'hidden';
  732. _iframe.style.border = 'none';
  733. _iframe.src = src;
  734. document.body.appendChild(_iframe);
  735. _cc.iframe = _iframe;
  736. };
  737. this.openSoftPhone = function (){
  738. //打开软电话
  739. var softPhoneUrl = "http://127.0.0.1:" + _cc.callConfig["localHostProxyPort"] +
  740. "/autoSetExtension?server=" + encodeURIComponent(_cc.callConfig["fsServer"].split(':')[0]) +
  741. "&port=" + _cc.callConfig["fsServer"].split(':')[1] +
  742. "&extnum=" + _cc.callConfig["extnum"] +
  743. "&pass=" + encodeURIComponent(_cc.callConfig["extPassword"]) +
  744. "&phoneType=" + _cc.callConfig["phoneType"] +
  745. "&version=" + encodeURIComponent(_cc.callConfig["localHostProxyVersion"]) +
  746. "&webPhoneUrl=" + encodeURIComponent(_cc.callConfig["webPhoneUrl"]) +
  747. "";
  748. console.log("softPhoneUrl:", softPhoneUrl);
  749. _cc.iframe.src = softPhoneUrl;
  750. };
  751. /**
  752. * 设置座席状态
  753. * @param status agentStatusEnum
  754. */
  755. this.setStatus = function (status) {
  756. var cmdInfo = {};
  757. cmdInfo.action="setAgentStatus";
  758. cmdInfo.body = {"cmd" : "setStatus", "args" : { "status" : status } };
  759. ws.send(JSON.stringify(cmdInfo));
  760. };
  761. //注销登录;
  762. this.logOff = function () {
  763. var cmdInfo = {};
  764. cmdInfo.action="setAgentStatus";
  765. cmdInfo.body = {"cmd" : "disconnect", "args" : { "cause": "disconnect request from js client." } };
  766. ws.send(JSON.stringify(cmdInfo));
  767. };
  768. /**
  769. * 在咨询失败的情况下使用该按钮,接回处于等待中的电话
  770. */
  771. this.stopCallWaitBtnClickUI = function () {
  772. var cmd = {};
  773. cmd.action="callWait";
  774. cmd.body = {"cmd" : "stop", "args" : {} };
  775. ws.send(JSON.stringify(cmd));
  776. };
  777. /**
  778. * 在咨询成功的情况下使用该按钮,把电话转接给专家坐席。
  779. */
  780. this.transferCallWaitBtnClickUI = function () {
  781. this.callControl("transferCallWait", {})
  782. };
  783. this.consultationBtnClickUI = function () {
  784. let transferType = "outer";
  785. let phoneNumber = $("#externalPhoneNumber").val().trim();
  786. if (phoneNumber === "") {
  787. transferType = "inner";
  788. var groupId = $("#transfer_to_groupIds").val();
  789. if ($.trim(groupId) == "") {
  790. alert("请选择业务组!");
  791. $("#transfer_to_groupIds").focus();
  792. return;
  793. }
  794. var member = $("#transfer_to_member").val();
  795. if ($.trim(member) == "") {
  796. alert("请选择要咨询的坐席成员!");
  797. $("#transfer_to_member").focus();
  798. return;
  799. }
  800. var selectText = $('#transfer_to_member option:selected').text();
  801. if (selectText.indexOf("空闲") == -1) {
  802. alert("请选择空闲的坐席成员!");
  803. $("#transfer_to_member").focus();
  804. return;
  805. }
  806. if (member == this.getOpNum()) {
  807. alert("不能咨询自己,请选择其他坐席成员!");
  808. return;
  809. }
  810. phoneNumber = member;
  811. }
  812. this.callControl("consultation", {"to": phoneNumber, "transferType": transferType});
  813. };
  814. /**
  815. * 处理通话转接按钮点击事件
  816. */
  817. this.transferBtnClickUI = function() {
  818. let transferType = "outer";
  819. let phoneNumber = $("#externalPhoneNumber").val().trim();
  820. if(phoneNumber === "") {
  821. transferType = "inner";
  822. var groupId = $("#transfer_to_groupIds").val();
  823. if ($.trim(groupId) === "") {
  824. alert("请选择转接的业务组!");
  825. $("#transfer_to_groupIds").focus();
  826. return;
  827. }
  828. var member = $("#transfer_to_member").val();
  829. if ($.trim(member) === "") {
  830. alert("请选择转接的坐席成员!");
  831. $("#transfer_to_member").focus();
  832. return;
  833. }
  834. var selectText = $('#transfer_to_member option:selected').text();
  835. if (selectText.indexOf("空闲") === -1) {
  836. alert("请选择空闲的坐席成员!");
  837. $("#transfer_to_member").focus();
  838. return;
  839. }
  840. if (member === this.getOpNum()) {
  841. alert("不能转给自己,请选择其他坐席成员!");
  842. return;
  843. }
  844. phoneNumber = member;
  845. }
  846. this.transferCall(phoneNumber, transferType);
  847. };
  848. /**
  849. * 处理通话转接
  850. * @param userCodeOrPhone 工号或者电话号码
  851. * @param transferType 转接类型:工号还是外部号码(inner or outer)
  852. */
  853. this.transferCall = function(userCodeOrPhone, transferType) {
  854. if(transferType === "inner") {
  855. if (userCodeOrPhone !== this.getOpNum()) {
  856. this.callControl("transferCall", {"to": userCodeOrPhone, "transferType" : "inner" })
  857. } else {
  858. console.error("cant not transfer call to yourself.")
  859. }
  860. }else{
  861. this.callControl("transferCall", {"to": userCodeOrPhone, "transferType" : "outer" })
  862. }
  863. };
  864. //挂机
  865. this.hangup = function() {
  866. this.callControl("endSession", {})
  867. };
  868. // 呼叫控制相关操作;
  869. this.callControl = function(action, argsObject){
  870. var sessionControl = {};
  871. sessionControl.action="call";
  872. sessionControl.body = {"cmd" : action, "args" : argsObject };
  873. ws.send(JSON.stringify(sessionControl));
  874. };
  875. this.checkCallConfirmed = function () {
  876. if(!_cc.getIsConnected()){
  877. console.log('请先上线.');
  878. return false;
  879. }
  880. if(!_cc.getCallConnected()){
  881. console.log('当前没有通话.');
  882. return false;
  883. }
  884. return true;
  885. };
  886. /**
  887. * send and play mp4 video file.
  888. */
  889. this.sendVideoFile = function (mp4FilePath) {
  890. if(!_cc.checkCallConfirmed()){
  891. return false;
  892. }
  893. if(typeof(mp4FilePath) == "undefined" || mp4FilePath.trim().length === 0){
  894. console.log("Parameter mp4FilePath is missing!")
  895. return false;
  896. }
  897. this.callControl(
  898. "playMp4File",
  899. { "mp4FilePath" : mp4FilePath }
  900. );
  901. return true;
  902. };
  903. /**
  904. * 发起视频通话邀请
  905. */
  906. this.reInviteVideoCall = function(){
  907. if(!_cc.checkCallConfirmed()){
  908. return false;
  909. }
  910. if(!_cc.getCanSendVideoReInvite()){
  911. console.log('cant not send video reInvite. ',
  912. 'Precondition is: Call is connected and callType is audio.');
  913. return false;
  914. }
  915. this.callControl(
  916. "reInviteVideo",
  917. {}
  918. );
  919. return true;
  920. };
  921. /**
  922. * 发起外呼
  923. * @param phoneNumber 被叫号码
  924. * @param callType 通话类型:视频通话、音频通话
  925. * @param videoLevel 视频通话的profile-level-id
  926. */
  927. this.call = function(phoneNumber, callType, videoLevel){
  928. if(typeof(videoLevel) == "undefined" || videoLevel.trim().length === 0){
  929. videoLevel = ccPhoneBarSocket.videoLevels.HD.levelId;
  930. console.log("auto default set videoLevel=", videoLevel);
  931. }
  932. if(typeof(callType) == "undefined" || callType.trim().length === 0){
  933. callType = "audio";
  934. console.log("auto default set callType=", callType);
  935. }
  936. console.log("call config videoLevel=" + videoLevel + ", callType=" + callType);
  937. if(phoneNumber==null || phoneNumber.length===0) {
  938. console.log('请输入外呼号码!');
  939. return;
  940. }
  941. if(phoneNumber.trim().length < 3){
  942. alert('请输入正确格式的外呼号码!');
  943. return;
  944. }
  945. if(!_cc.getIsConnected()){
  946. console.log('请先上线.');
  947. return;
  948. }
  949. let outboundInfo = {
  950. "gatewayList": _cc.callConfig.gatewayList,
  951. 'destPhone': phoneNumber,
  952. 'gatewayEncrypted' : _cc.callConfig.gatewayEncrypted,
  953. 'useSameAudioCodeForOutbound' : _cc.callConfig.useSameAudioCodeForOutbound,
  954. 'callType' : callType,
  955. 'videoLevel' : videoLevel
  956. };
  957. this.callControl(
  958. "startSession",
  959. outboundInfo
  960. );
  961. };
  962. this.callEx = function(phoneNumber){
  963. if(phoneNumber == null || phoneNumber.length === 0) {
  964. console.log('请输入外呼号码!');
  965. return;
  966. }
  967. if(!_cc.getIsConnected()){
  968. _cc.connect();
  969. _cc.on(ccPhoneBarSocket.eventList.ws_connected, function(){
  970. _cc.off(ccPhoneBarSocket.eventList.ws_connected); //取消事件订阅
  971. _cc.call(phoneNumber);
  972. });
  973. return;
  974. }
  975. _cc.call(phoneNumber);
  976. };
  977. /************************ 以下是网页工具条ui代码 ************************/
  978. /**
  979. * 根据服务器响应状态码去查找action
  980. * @param code
  981. * @returns {string}
  982. */
  983. ccPhoneBarSocket.findItemByCode = function(code){
  984. for(var item in ccPhoneBarSocket.eventListWithTextInfo ){
  985. if(ccPhoneBarSocket.eventListWithTextInfo[item].code === code){
  986. return ccPhoneBarSocket.eventListWithTextInfo[item];
  987. }
  988. }
  989. };
  990. /**
  991. * 服务器响应状态枚举值;
  992. */
  993. ccPhoneBarSocket.eventListWithTextInfo = {
  994. "ws_connected": { "code": 200, msg:"已签入",
  995. btn_text:[{id:"#onLineBtn",name:"签出"}],
  996. enabled_btn:['#setFree','#callBtn','#onLineBtn', '#consultationBtn']
  997. },
  998. "ws_disconnected": { "code" : 202, msg:"服务器连接断开",
  999. btn_text:[{id:"#onLineBtn",name:"签入"}],
  1000. enabled_btn:['#onLineBtn']
  1001. },
  1002. "user_login_on_other_device": { "code" : 201, msg:"用户已在其他设备登录",
  1003. btn_text:[{id:"#onLineBtn",name:"签入"}],
  1004. enabled_btn:['#onLineBtn']
  1005. },
  1006. "request_args_error":{ "code" : 400, msg:"客户端请求参数错误",
  1007. btn_text:[],
  1008. enabled_btn:[]
  1009. },
  1010. "server_error":{ "code" : 500, msg:"服务器内部错误",
  1011. btn_text:[],
  1012. enabled_btn:[]
  1013. },
  1014. "caller_answered":{ "code" : 600, msg:"分机已接通",
  1015. btn_text:[],
  1016. enabled_btn:['#resetStatus', '#hangUpBtn', '#transferBtn', '#holdBtn', '#consultationBtn']
  1017. },
  1018. "caller_hangup":{ "code" : 601, msg:"分机已挂断",
  1019. btn_text:[],
  1020. enabled_btn:['#onLineBtn', '#resetStatus', '#callBtn', '#setFree', '#consultationBtn' ]
  1021. },
  1022. "caller_busy":{ "code" : 602, msg:"分机忙,上一通电话未挂断",
  1023. btn_text:[],
  1024. enabled_btn:['#onLineBtn', '#resetStatus', '#callBtn', '#setFree', '#consultationBtn']
  1025. },
  1026. "caller_not_login":{ "code" : 603, msg:"分机未登录,请检查",
  1027. btn_text:[],
  1028. enabled_btn:['#onLineBtn', '#resetStatus', '#callBtn', '#setFree', '#consultationBtn']
  1029. },
  1030. "caller_respond_timeout":{ "code" : 604, msg:"分机未应答超时,请重新打开分机",
  1031. btn_text:[],
  1032. enabled_btn:['#onLineBtn', '#resetStatus', '#callBtn', '#setFree', '#consultationBtn']
  1033. },
  1034. "callee_answered":{ "code" : 605, msg:"被叫已接通",
  1035. btn_text:[],
  1036. enabled_btn:['#resetStatus', '#hangUpBtn', '#transferBtn', '#holdBtn', '#consultationBtn' ]
  1037. },
  1038. "callee_hangup":{ "code" : 606, msg:"通话结束",
  1039. btn_text:[],
  1040. enabled_btn:['#onLineBtn', '#resetStatus', '#callBtn', '#setFree' , '#consultationBtn']
  1041. },
  1042. "callee_ringing":{ "code" : 607, msg:"被叫振铃中",
  1043. btn_text:[],
  1044. enabled_btn:['#resetStatus', '#hangUpBtn', '#transferBtn', '#consultationBtn']
  1045. },
  1046. "status_changed":{ "code" : 608, msg:"状态已改变",
  1047. btn_text:[],
  1048. enabled_btn:[ ]
  1049. },
  1050. "free":{ "code" : 0, msg:"空闲中",
  1051. btn_text:[],
  1052. enabled_btn:['#setBusy','#onLineBtn', '#consultationBtn']
  1053. },
  1054. "busy":{ "code" : 1, msg:"忙碌",
  1055. btn_text:[],
  1056. enabled_btn:['#setFree', '#onLineBtn', '#callBtn', '#consultationBtn'] // '#transferBtn'
  1057. },
  1058. "customer_channel_hold" : { "code" : 623, msg:"通话已保持.",
  1059. btn_text:[],
  1060. enabled_btn:['#setFree', '#callBtn', '#unHoldBtn', '#consultationBtn' ]
  1061. },
  1062. "customer_channel_unhold" : { "code" : 624, msg:"通话已接回.",
  1063. btn_text:[],
  1064. enabled_btn:[ '#hangUpBtn', '#holdBtn' ]
  1065. }
  1066. };
  1067. ccPhoneBarSocket.phone_buttons = ['#setFree', '#setBusy', '#callBtn','#hangUpBtn' , '#resetStatus' ,'#onLineBtn', '#transferBtn', '#holdBtn', '#unHoldBtn', '#consultationBtn'];
  1068. // 更新状态显示
  1069. this.updatePhoneBar = function (msg, status_key) {
  1070. if(!_cc.callConfig.useDefaultUi){
  1071. console.log("callConfig.useDefaultUi = false , 已禁用默认ui工具条按钮.");
  1072. return;
  1073. }
  1074. if (msg) {
  1075. $("#callStatus").text(msg.msg);
  1076. }
  1077. var status_info = ccPhoneBarSocket.findItemByCode(status_key);
  1078. if (!status_info) {
  1079. return;
  1080. }
  1081. if(status_info.code === ccPhoneBarSocket.eventListWithTextInfo.status_changed.code){
  1082. if(msg.object.status === ccPhoneBarSocket.agentStatusEnum.free){
  1083. status_info = ccPhoneBarSocket.eventListWithTextInfo.free;
  1084. }else{
  1085. status_info = ccPhoneBarSocket.eventListWithTextInfo.busy;
  1086. }
  1087. }
  1088. // 判断当前是否为状态改变的事件;
  1089. // 显示预设的消息;
  1090. var msgSet = status_info.msg;
  1091. if(msgSet && msgSet.length > 0){
  1092. $("#callStatus").text(msgSet);
  1093. }
  1094. var btn_text = status_info.btn_text;
  1095. var enabled_btn = status_info.enabled_btn;
  1096. if (btn_text) {
  1097. $.each(btn_text, function (i, d) {
  1098. $(d.id).next().text(d.name);
  1099. });
  1100. }
  1101. if (enabled_btn.length === 0) {
  1102. return;
  1103. }
  1104. var all_btn = ccPhoneBarSocket.phone_buttons;
  1105. for (var i in all_btn) {
  1106. var idx = $.inArray(all_btn[i], enabled_btn);
  1107. if (idx < 0) {
  1108. $(all_btn[i]).removeClass('on');
  1109. } else {
  1110. $(enabled_btn[idx]).addClass('on');
  1111. }
  1112. }
  1113. };
  1114. /**
  1115. * 初始化电话工具条ui按钮;
  1116. */
  1117. this.initPhoneBarUI = function () {
  1118. window.onbeforeunload = function () {
  1119. if (!confirm('关闭网页将导致您无法接听电话,确定要关闭吗 ?')) return false;
  1120. };
  1121. $("#unHoldBtnLi").hide();
  1122. if(!_cc.callConfig.useDefaultUi){
  1123. console.log("callConfig.useDefaultUi = false , 已禁用默认ui工具条按钮.");
  1124. return;
  1125. }
  1126. $('#conferenceBtn').on('click', function () {
  1127. if(!_cc.getIsConnected()){
  1128. console.log('请先上线.');
  1129. return;
  1130. }
  1131. var confObjId = document.getElementById("conference_area");
  1132. if(confObjId.style.display === "block"){
  1133. confObjId.style.display = "none";
  1134. }else{
  1135. confObjId.style.display = "block";
  1136. }
  1137. });
  1138. $('#callBtn').on('click', function () {
  1139. if ($(this).hasClass('on')) {
  1140. var destPhone = $.trim($("#ccphoneNumber").val());
  1141. var videoLevel = document.getElementById("videoLevelSelect").value;
  1142. var callType = document.forms[0].callType.value;
  1143. _cc.call(destPhone, callType, videoLevel);
  1144. }
  1145. });
  1146. $('#setFree').on('click', function () {
  1147. if ($(this).hasClass('on')) {
  1148. _cc.setStatus(ccPhoneBarSocket.agentStatusEnum.free);
  1149. }
  1150. });
  1151. $('#setBusy').on('click', function () {
  1152. if ($(this).hasClass('on')) {
  1153. _cc.setStatus(ccPhoneBarSocket.agentStatusEnum.busy);
  1154. }
  1155. });
  1156. $('#setBusySubList').on('change', function () {
  1157. let itemValue = $('#setBusySubList').val();
  1158. console.log('set busy subStatus', itemValue);
  1159. _cc.setStatus(itemValue);
  1160. });
  1161. $('#hangUpBtn').on('click', function () {
  1162. if ($(this).hasClass('on')) {
  1163. _cc.hangup();
  1164. }
  1165. });
  1166. $('#holdBtn').on('click', function () {
  1167. if ($(this).hasClass('on')) {
  1168. _cc.holdCall();
  1169. }
  1170. });
  1171. $('#unHoldBtn').on('click', function () {
  1172. if ($(this).hasClass('on')) {
  1173. _cc.unHoldCall();
  1174. }
  1175. });
  1176. $("#doTransferBtn").hide();
  1177. $('#transferBtn').on('click', function () {
  1178. if ($(this).hasClass('on')) {
  1179. if(!_cc.getIsConnected()){
  1180. console.log('请先上线.');
  1181. return;
  1182. }
  1183. var transferArea = document.getElementById("transfer_area");
  1184. if(transferArea.style.display === "block"){
  1185. transferArea.style.display = "none";
  1186. _phoneBar.unSubscribeAgentList();
  1187. $("#doTransferBtn").hide();
  1188. $("#doConsultationBtn").hide();
  1189. }else{
  1190. transferArea.style.display = "block";
  1191. populateGroupIdOptions();
  1192. _phoneBar.subscribeAgentList();
  1193. $("#doTransferBtn").show();
  1194. $("#doConsultationBtn").hide();
  1195. }
  1196. }
  1197. });
  1198. $("#stopCallWait").hide();
  1199. $("#transferCallWait").hide();
  1200. $("#doConsultationBtn").hide();
  1201. $('#consultationBtn').on('click', function () {
  1202. if ($(this).hasClass('on')) {
  1203. if(!_cc.getIsConnected()){
  1204. console.log('请先上线.');
  1205. return;
  1206. }
  1207. var transferArea = document.getElementById("transfer_area");
  1208. if(transferArea.style.display === "block"){
  1209. transferArea.style.display = "none";
  1210. _phoneBar.unSubscribeAgentList();
  1211. $("#doConsultationBtn").hide();
  1212. $("#doTransferBtn").hide();
  1213. }else{
  1214. transferArea.style.display = "block";
  1215. populateGroupIdOptions();
  1216. _phoneBar.subscribeAgentList();
  1217. $("#doConsultationBtn").show();
  1218. $("#doTransferBtn").hide();
  1219. }
  1220. }
  1221. });
  1222. $('#onLineBtn').on('click', function () {
  1223. if ($(this).hasClass('on')) {
  1224. if (_cc.getIsConnected()) {
  1225. _cc.disconnect();
  1226. } else {
  1227. _cc.connect();
  1228. }
  1229. }else {
  1230. alert('当前不允许签出!');
  1231. }
  1232. });
  1233. $('#resetStatus').on('click', function () {
  1234. window.onbeforeunload = null;
  1235. location.reload();
  1236. });
  1237. //拨号文本框;收到键盘回车事件之后立即拨号
  1238. $("#ccphoneNumber").keydown(function (e) {
  1239. var curKey = e.which;
  1240. if (curKey === 13) {
  1241. var destPhone = $.trim($("#ccphoneNumber").val());
  1242. var videoLevel = document.getElementById("videoLevelSelect").value;
  1243. var callType = document.forms[0].callType.value;
  1244. _cc.call(destPhone,callType, videoLevel);
  1245. return false;
  1246. }
  1247. });
  1248. //ESC按键挂机功能支持
  1249. $(document).keyup(function (e) {
  1250. var key = e.which;
  1251. if (key === 27) {
  1252. console.log('按下了ESC键, 即将发送挂机指令.');
  1253. if(_cc.getIsConnected()){
  1254. if(_cc.callConfig["useDefaultUi"]) {
  1255. if ($('#hangUpBtn').hasClass('on')) {
  1256. _cc.hangup();
  1257. }
  1258. }else{
  1259. _cc.hangup();
  1260. }
  1261. }
  1262. }
  1263. });
  1264. };
  1265. /**
  1266. * 保持通话
  1267. */
  1268. this.holdCall = function(){
  1269. var cmd = {};
  1270. cmd.action="callHold";
  1271. cmd.body = {"cmd" : "hold", "args" : {} };
  1272. ws.send(JSON.stringify(cmd));
  1273. };
  1274. /**
  1275. * 接回保持的通话
  1276. */
  1277. this.unHoldCall = function(){
  1278. var cmd = {};
  1279. cmd.action="callHold";
  1280. cmd.body = {"cmd" : "unhold", "args" : {} };
  1281. ws.send(JSON.stringify(cmd));
  1282. };
  1283. /**
  1284. * 订阅坐席状态列表
  1285. */
  1286. this.subscribeAgentList = function(){
  1287. var cmd = {};
  1288. cmd.action="pollAgentList";
  1289. cmd.body = {"cmd" : "subscribe", "args" : {} };
  1290. ws.send(JSON.stringify(cmd));
  1291. _cc.subscribeAgentListStarted = true;
  1292. };
  1293. /**
  1294. * 取消订阅坐席状态列表
  1295. */
  1296. this.unSubscribeAgentList = function(){
  1297. if(_cc.subscribeAgentListStarted) {
  1298. _cc.subscribeAgentListStarted = false;
  1299. _cc.callConfig.agentList = null;
  1300. var cmd = {};
  1301. cmd.action = "pollAgentList";
  1302. cmd.body = {"cmd": "unSubscribe", "args": {}};
  1303. ws.send(JSON.stringify(cmd));
  1304. }
  1305. };
  1306. /************************* 以下是电话会议相关 ***************************/
  1307. /**
  1308. * 启动会议; 仅限使用默认UI场景下使用;
  1309. */
  1310. this.conferenceStartBtnUI = function(customerName){
  1311. var callType = document.getElementById("conf_call_type").value;
  1312. var confTemplate = document.getElementById("conf_template").value;
  1313. var layOut = document.getElementById("conf_layout").value;
  1314. _cc.setStatusBusy();
  1315. // 禁用外呼按钮
  1316. $("#callBtn").removeClass('on');
  1317. // 禁用置闲按钮
  1318. $("#setFree").removeClass('on');
  1319. // 禁用签出按钮
  1320. $("#onLineBtn").removeClass('on');
  1321. document.getElementById("startConference").setAttribute("disabled", "true");
  1322. if(_cc.getCallConnected()) {
  1323. let tips = "是否把当前通话转换为会议 ?";
  1324. console.log(tips);
  1325. if(confirm(tips)) {
  1326. _cc.transferToConference(layOut, confTemplate, callType, customerName);
  1327. }else{
  1328. document.getElementById("startConference").removeAttribute("disabled");
  1329. }
  1330. }else {
  1331. _cc.conferenceStart(layOut, confTemplate, callType);
  1332. }
  1333. };
  1334. /**
  1335. * 添加会议成员; 仅限使用默认UI场景下使用;
  1336. */
  1337. this.conferenceAddMemberBtnUI = function (reInvite, memberPhoneParam, memberNameParam) {
  1338. var memberName = "";
  1339. var memberPhone = "";
  1340. var memberCallType = $.trim(document.getElementById("member_call_type").value);
  1341. var memberVideoLevel = $.trim(document.getElementById("member_video_level").value);
  1342. if(reInvite === 0) {
  1343. memberName = $("#member_name").val();
  1344. memberPhone = $("#member_phone").val();
  1345. if (memberName.length === 0 || $.trim(memberName) === "") {
  1346. alert('请填写参会者姓名!');
  1347. return;
  1348. }
  1349. if (memberPhone.length === 0 || $.trim(memberPhone) === "") {
  1350. alert('请填写参会者手机号!');
  1351. return;
  1352. }
  1353. memberName = $.trim(memberName);
  1354. memberPhone = $.trim(memberPhone);
  1355. // 使用会员成员html模版添加新成员
  1356. var templateObj = document.getElementById("conf_member_template");
  1357. var existMember = document.getElementById("conf_member_" + memberPhone) != null;
  1358. if (existMember) {
  1359. alert('会议成员已经存在,请不要重复添加!');
  1360. return;
  1361. }
  1362. var memberHtmlItem = templateObj.innerHTML;
  1363. memberHtmlItem = memberHtmlItem.replace(new RegExp("{member_name}", "gm"), memberName);
  1364. memberHtmlItem = memberHtmlItem.replace(new RegExp("{member_phone}", "gm"), memberPhone);
  1365. memberHtmlItem = memberHtmlItem.replace(new RegExp("{member_status}", "gm"), "即将呼叫");
  1366. var li = document.createElement("li");
  1367. li.setAttribute("id", "conf_member_" + memberPhone);
  1368. li.setAttribute("class", "conf_member_item_row");
  1369. li.innerHTML = memberHtmlItem;
  1370. _cc.insertAfter(li, templateObj);
  1371. $("#member_name").val('');
  1372. $("#member_phone").val('');
  1373. }else{
  1374. memberName = memberNameParam;
  1375. memberPhone = memberPhoneParam;
  1376. }
  1377. // 隐藏 mute及 vmute按钮
  1378. var memberItemId = "#conf_member_" + memberPhone;
  1379. $(".conf_mute", $(memberItemId)).find("img").hide();
  1380. $(".conf_vmute", $(memberItemId)).find("img").hide();
  1381. $(".conf_re_invite", $(memberItemId)).hide();
  1382. $(".conf_status", $(memberItemId)).html("即将呼叫");
  1383. _cc.conferenceAddMembers( [
  1384. {"name": memberName, "phone": memberPhone, "callType" : memberCallType, "videoLevel": memberVideoLevel}
  1385. ]);
  1386. };
  1387. /**
  1388. * 从现有通话添加会议成员;
  1389. */
  1390. this.conferenceAddMemberFromExistCall = function (memberName, memberPhone) {
  1391. if (memberName.length === 0 || $.trim(memberName) === "") {
  1392. alert('请填写参会者姓名!');
  1393. return;
  1394. }
  1395. if (memberPhone.length === 0 || $.trim(memberPhone) === "") {
  1396. alert('请填写参会者手机号!');
  1397. return;
  1398. }
  1399. memberName = $.trim(memberName);
  1400. memberPhone = $.trim(memberPhone);
  1401. // 使用html模版添加新成员
  1402. var templateObj = document.getElementById("conf_member_template");
  1403. var existMember = document.getElementById("conf_member_" + memberPhone) != null;
  1404. if (existMember) {
  1405. alert('会议成员已经存在,请不要重复添加!');
  1406. return;
  1407. }
  1408. var memberHtmlItem = templateObj.innerHTML;
  1409. memberHtmlItem = memberHtmlItem.replace(new RegExp("{member_name}", "gm"), memberName);
  1410. memberHtmlItem = memberHtmlItem.replace(new RegExp("{member_phone}", "gm"), memberPhone);
  1411. memberHtmlItem = memberHtmlItem.replace(new RegExp("{member_status}", "gm"), "即将呼叫");
  1412. var li = document.createElement("li");
  1413. li.setAttribute("id", "conf_member_" + memberPhone);
  1414. li.setAttribute("class", "conf_member_item_row");
  1415. li.innerHTML = memberHtmlItem;
  1416. _cc.insertAfter(li, templateObj);
  1417. // 隐藏 mute及 vmute按钮
  1418. var memberItemId = "#conf_member_" + memberPhone;
  1419. $(".conf_mute", $(memberItemId)).find("img").show();
  1420. $(".conf_vmute", $(memberItemId)).find("img").show();
  1421. $(".conf_re_invite", $(memberItemId)).hide();
  1422. $(".conf_status", $(memberItemId)).html("通话中").css("color", "green");
  1423. };
  1424. /**
  1425. * 通话转为会议
  1426. * @param layOut
  1427. * @param confTemplate
  1428. * @param callType
  1429. * @param customerName
  1430. */
  1431. this.transferToConference = function (layOut, confTemplate, callType, customerName) {
  1432. console.log("try to transferToConference: ", layOut, confTemplate, callType, customerName);
  1433. _cc.callControl(
  1434. "transferToConference",
  1435. {
  1436. "layOut": layOut,
  1437. 'callType': callType,
  1438. 'confTemplate': confTemplate,
  1439. 'customerName' : customerName
  1440. }
  1441. );
  1442. };
  1443. /**
  1444. * 主持人启动电话会议
  1445. * @param layOut 会议布局
  1446. * @param confTemplate 会议模版
  1447. * @param callType 会议类型
  1448. */
  1449. this.conferenceStart = function(layOut, confTemplate, callType) {
  1450. console.log("正常发起多方通话");
  1451. var cmd = {};
  1452. cmd.action = "conference";
  1453. cmd.body = {"method": "startconf", "args": {
  1454. "layOut": layOut,
  1455. 'callType': callType,
  1456. 'confTemplate': confTemplate
  1457. }};
  1458. ws.send(JSON.stringify(cmd));
  1459. };
  1460. /**
  1461. * VMute会议成员(不显示视频);
  1462. **/
  1463. this.conferenceVMuteMember = function(members) {
  1464. // single phone
  1465. if(typeof(members) === "string") {
  1466. this.conferenceControl("vmute",
  1467. [
  1468. {"phone": members}
  1469. ]
  1470. );
  1471. }else{
  1472. // multiple phones array, e.g.: [ {"phone": '15005600321'}, {"phone": '15005600323'} ]
  1473. this.conferenceControl("vmute", members);
  1474. }
  1475. };
  1476. /**
  1477. * UnVMute会议成员(显示视频);
  1478. **/
  1479. this.conferenceUnVMuteMember = function(members) {
  1480. // single phone
  1481. if(typeof(members) === "string") {
  1482. this.conferenceControl("unvmute",
  1483. [
  1484. {"phone": members}
  1485. ]
  1486. );
  1487. }else{
  1488. // multiple phones array, e.g.: [ {"phone": '15005600321'}, {"phone": '15005600323'} ]
  1489. this.conferenceControl("unvmute", members);
  1490. }
  1491. };
  1492. /**
  1493. * 禁言会议成员
  1494. **/
  1495. this.conferenceMuteMember = function(members) {
  1496. // single phone
  1497. if(typeof(members) === "string") {
  1498. this.conferenceControl("mute",
  1499. [
  1500. {"phone": members}
  1501. ]
  1502. );
  1503. }else{
  1504. // multiple phones array, e.g.: [ {"phone": '15005600321'}, {"phone": '15005600323'} ]
  1505. this.conferenceControl("mute", members);
  1506. }
  1507. };
  1508. /**
  1509. * 解禁会议成员
  1510. **/
  1511. this.conferenceUnMuteMember = function(members) {
  1512. // single phone
  1513. if(typeof(members) === "string") {
  1514. this.conferenceControl("unmute",
  1515. [
  1516. {"phone": members}
  1517. ]
  1518. );
  1519. }else{
  1520. // multiple phones array, e.g.: [ {"phone": '15005600321'}, {"phone": '15005600323'} ]
  1521. this.conferenceControl("unmute", members);
  1522. }
  1523. };
  1524. /**
  1525. * 增加会议成员
  1526. **/
  1527. this.conferenceAddMembers = function(members) {
  1528. this.conferenceControl("add", members)
  1529. };
  1530. /**
  1531. * 移除会议成员
  1532. **/
  1533. this.conferenceRemoveMembers = function(members) {
  1534. // single phone
  1535. if(typeof(members) === "string") {
  1536. var memberItemObj = $("#conf_member_" + members);
  1537. if($(".conf_status", memberItemObj).text() === "通话中") {
  1538. this.conferenceControl("remove",
  1539. [
  1540. {"phone": members}
  1541. ]
  1542. );
  1543. }
  1544. memberItemObj.remove();
  1545. }else{
  1546. // multiple phones array, e.g.: [ {"phone": '15005600321'}, {"phone": '15005600323'} ]
  1547. this.conferenceControl("remove", members);
  1548. }
  1549. };
  1550. /**
  1551. * 结束电话会议
  1552. **/
  1553. this.conferenceEnd = function() {
  1554. this.conferenceControl("endconf", [])
  1555. };
  1556. /**
  1557. * 电话会议控制相关操作
  1558. * @param action 操作
  1559. * @param phoneList 会议成员
  1560. */
  1561. this.conferenceControl = function (action, phoneList) {
  1562. var cmd = {};
  1563. cmd.action = "conference";
  1564. cmd.body = { "method": action, "memberList": phoneList };
  1565. ws.send(JSON.stringify(cmd));
  1566. };
  1567. /************************* 以下是通话监听相关 ***************************/
  1568. /**
  1569. * 拉取监听的通话列表
  1570. */
  1571. this.callMonitorDataPull = function (){
  1572. var cmd = {};
  1573. cmd.action = "monitorData";
  1574. cmd.body = {};
  1575. ws.send(JSON.stringify(cmd));
  1576. };
  1577. /**
  1578. * 拉取排队中的电话列表
  1579. */
  1580. this.inboundCallQueuePull = function (){
  1581. var cmd = {};
  1582. cmd.action = "inboundMonitorData";
  1583. cmd.body = {};
  1584. ws.send(JSON.stringify(cmd));
  1585. };
  1586. // 构造通话监听参数
  1587. this.monitorControl = function(action, argsObject){
  1588. var sessionControl = {};
  1589. sessionControl.action="callMonitor";
  1590. sessionControl.body = {"cmd" : action, "args" : argsObject };
  1591. ws.send(JSON.stringify(sessionControl));
  1592. };
  1593. /**
  1594. * 通话监听
  1595. * @param { 通话id } callId
  1596. * @returns
  1597. */
  1598. this.callMonitorStart = function(callId){
  1599. if(callId == null || callId.length === 0) {
  1600. console.log('请提供待监听电话的 callId !');
  1601. return;
  1602. }
  1603. if(!_cc.getIsConnected()){
  1604. console.log('请先上线.');
  1605. return;
  1606. }
  1607. this.monitorControl(
  1608. "startMonitoring",
  1609. {
  1610. 'callSpyId': callId
  1611. }
  1612. );
  1613. };
  1614. /**
  1615. * 结束监听
  1616. */
  1617. this.callMonitorEnd = function(){
  1618. if(!_cc.getIsConnected()){
  1619. console.log('请先上线.');
  1620. return;
  1621. }
  1622. this.monitorControl(
  1623. "endMonitoring",{}
  1624. );
  1625. };
  1626. }