ccPhoneBarSocket.js 51 KB

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