ccPhoneBarSocket.js 51 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853
  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. * in conference
  524. */
  525. "conference" : 6,
  526. /**
  527. * acd agnet is in lockStatus
  528. */
  529. "lockStatus" : 7
  530. };
  531. //定义视频level-id
  532. ccPhoneBarSocket.videoLevels = {
  533. "Smooth" : { "levelId" : "42e00b", "description" : "流畅" },
  534. "Smooth2" : { "levelId" : "42e00c", "description" : "流畅+" },
  535. "Smooth3" : { "levelId" : "42e00d", "description" : "流畅++" },
  536. "Clear" : { "levelId" : "42e014", "description" : "清晰" },
  537. "Clear2" : { "levelId" : "42e015", "description" : "清晰+" },
  538. "Clear3" : { "levelId" : "42e016", "description" : "清晰++" },
  539. "HD" : { "levelId" : "42e01e", "description" : "高清" },
  540. "HD2" : { "levelId" : "42e01f", "description" : "高清+" }
  541. };
  542. /**
  543. * 系统事件列表;
  544. * @type {{}}
  545. */
  546. ccPhoneBarSocket.eventList = {
  547. // 当音频通话已建立
  548. "on_audio_call_connected" : "100",
  549. "on_video_call_connected" : "101",
  550. // websocketServer连接成功
  551. "ws_connected": "200",
  552. // 当前用户已在其他设备登录;
  553. "user_login_on_other_device" : "201",
  554. // 用户下线
  555. "ws_disconnected" : "202",
  556. // 通话状态发生改变 [监听的数据]
  557. "call_session_status_data_changed" : "203",
  558. // 坐席状态列表发生改变
  559. "agent_status_data_changed" : "205",
  560. //请求参数错误
  561. "request_args_error" : "400",
  562. // unauthorized
  563. "unauthorized" : "401",
  564. //服务器内部错误
  565. "server_error" : "500",
  566. // 语音编码不匹配
  567. "server_error_audio_codec_not_match" : "501",
  568. // 主叫接通
  569. "caller_answered" : "600",
  570. // 主叫挂断
  571. "caller_hangup" : "601",
  572. // 主叫忙; 上一通电话未挂机
  573. "caller_busy" : "602",
  574. //主叫未登录
  575. "caller_not_login" : "603",
  576. //主叫应答超时
  577. "caller_respond_timeout" : "604",
  578. // 被叫接通
  579. "callee_answered" : "605",
  580. // 被叫挂断
  581. "callee_hangup" : "606",
  582. //被叫振铃
  583. "callee_ringing" : "607",
  584. // 座席状态改变
  585. "status_changed" : "608",
  586. // 一个完整的外呼任务结束: [可能尝试了一个或多个网关]
  587. "outbound_finished" : "611",
  588. // 预测外呼,分配的来电;
  589. "PREDICTIVE_CALL_INBOUND" : "612",
  590. // ACD队列分配的新来电
  591. "new_inbound_call" : "613",
  592. // 当前业务组实时排队人数
  593. "acd_group_queue_number" : "615",
  594. /**
  595. * 收到转接的来电请求
  596. */
  597. "transfer_call_recv" : "616",
  598. /**
  599. * 锁定坐席失败
  600. */
  601. "lock_agent_fail" : "617",
  602. /**
  603. * 通话已经转接成功
  604. */
  605. "transfer_call_success" : "618",
  606. /**
  607. * 产生asr语音识别结果
  608. */
  609. "asr_result_generate" : "619",
  610. /**
  611. * ASR语音识别流程结束(坐席侧)
  612. */
  613. "asr_process_end_agent" : "620",
  614. /**
  615. * ASR语音识别流程结束(客户侧)
  616. */
  617. "asr_process_end_customer" : "621",
  618. "asr_process_started" : "622",
  619. /**
  620. * customer call session hold.
  621. */
  622. "customer_channel_hold" : "623",
  623. /**
  624. * customer call session unHold.
  625. */
  626. "customer_channel_unhold" : "624",
  627. /**
  628. * customer call session on hold is hangup.
  629. */
  630. "customer_on_hold_hangup" : "625",
  631. "inner_consultation_request" : "626",
  632. /**
  633. * customer call session on call-wait.
  634. */
  635. "customer_channel_call_wait" : "627",
  636. /**
  637. * customer call session off call-wait.
  638. */
  639. "customer_channel_off_call_wait" : "628",
  640. /**
  641. * customer call session on call-wait is hangup.
  642. */
  643. "customer_on_call_wait_hangup" : "629",
  644. /**
  645. * extension on line event
  646. */
  647. "extension_on_line" : "630",
  648. /**
  649. * extension off line event
  650. */
  651. "extension_off_line" : "631",
  652. /**
  653. * Notify the agent that the call consultation has started.
  654. */
  655. "inner_consultation_start" : "632",
  656. /**
  657. * Notify the agent that the call consultation has stopped.
  658. */
  659. "inner_consultation_stop" : "633",
  660. "extension_cannot_connected" : "634",
  661. /**
  662. * 多人电话会议,重复的被叫 ,
  663. */
  664. "conference_repeat_callee" : "660" ,
  665. /**
  666. * 多人电话会议,呼叫成员超时 ,
  667. */
  668. "CONFERENCE_CALL_MODERATOR_TIMEOUT" : "661" ,
  669. /**
  670. * 多人电话会议,成员接通 ,
  671. */
  672. "CONFERENCE_MEMBER_ANSWERED" : "662" ,
  673. /**
  674. * 多人电话会议,成员挂机 ,
  675. */
  676. "CONFERENCE_MEMBER_HANGUP" : "663" ,
  677. /**
  678. * 多人电话会议,成员禁言成功 ,
  679. */
  680. "CONFERENCE_MEMBER_MUTED_SUCCESS" : "666" ,
  681. /**
  682. * 多人电话会议,成员禁言失败 ,
  683. */
  684. "CONFERENCE_MEMBER_MUTED_FAILED" : "665" ,
  685. /**
  686. * 多人电话会议,成员解除禁言成功 ,
  687. */
  688. "CONFERENCE_MEMBER_UNMUTED_SUCCESS" : "667" ,
  689. /**
  690. * 多人电话会议,成员解除禁言失败 ,
  691. */
  692. "CONFERENCE_MEMBER_UNMUTED_FAILED" : "668" ,
  693. /**
  694. * 多人电话会议,会议成员不存在,无法执行相关操作:
  695. */
  696. "CONFERENCE_MEMBER_NOT_EXISTS" : "669" ,
  697. /**
  698. * 多人电话会议,主持人重置会议 ,
  699. */
  700. "CONFERENCE_MODERATOR_RESET" : "670" ,
  701. /**
  702. * 多人电话会议,主持人接通 ,
  703. */
  704. "CONFERENCE_MODERATOR_ANSWERED" : "671" ,
  705. /**
  706. * 多人电话会议,主持人挂机,会议结束 ,
  707. */
  708. "CONFERENCE_MODERATOR_HANGUP" : "672",
  709. /*
  710. * 成员视频禁用成功
  711. */
  712. "CONFERENCE_MEMBER_VMUTED_SUCCESS" : "674",
  713. /*
  714. * 成员解除视频禁用成功
  715. */
  716. "CONFERENCE_MEMBER_UnVMUTED_SUCCESS" : "676",
  717. /**
  718. * 多人电话会议,成员解除视频禁用失败;
  719. */
  720. "CONFERENCE_MEMBER_UnVMUTED_FAILED" : "677",
  721. /**
  722. * 成功把通话转接到多人视频会议
  723. */
  724. "CONFERENCE_TRANSFER_SUCCESS_FROM_EXISTED_CALL" : "678",
  725. /**
  726. * outbound start event
  727. */
  728. "OUTBOUND_START" : "679"
  729. };
  730. this.createIframe = function(src){
  731. var _iframe = document.createElement("iframe");
  732. _iframe.style.width = '0';
  733. _iframe.style.height = '0';
  734. _iframe.style.margin = '0';
  735. _iframe.style.padding = '0';
  736. _iframe.style.overflow = 'hidden';
  737. _iframe.style.border = 'none';
  738. _iframe.src = src;
  739. document.body.appendChild(_iframe);
  740. _cc.iframe = _iframe;
  741. };
  742. this.openSoftPhone = function (){
  743. //打开软电话
  744. var softPhoneUrl = "http://127.0.0.1:" + _cc.callConfig["localHostProxyPort"] +
  745. "/autoSetExtension?server=" + encodeURIComponent(_cc.callConfig["fsServer"].split(':')[0]) +
  746. "&port=" + _cc.callConfig["fsServer"].split(':')[1] +
  747. "&extnum=" + _cc.callConfig["extnum"] +
  748. "&pass=" + encodeURIComponent(_cc.callConfig["extPassword"]) +
  749. "&phoneType=" + _cc.callConfig["phoneType"] +
  750. "&version=" + encodeURIComponent(_cc.callConfig["localHostProxyVersion"]) +
  751. "&webPhoneUrl=" + encodeURIComponent(_cc.callConfig["webPhoneUrl"]) +
  752. "";
  753. console.log("softPhoneUrl:", softPhoneUrl);
  754. _cc.iframe.src = softPhoneUrl;
  755. };
  756. /**
  757. * 设置座席状态
  758. * @param status agentStatusEnum
  759. */
  760. this.setStatus = function (status) {
  761. var cmdInfo = {};
  762. cmdInfo.action="setAgentStatus";
  763. cmdInfo.body = {"cmd" : "setStatus", "args" : { "status" : status } };
  764. ws.send(JSON.stringify(cmdInfo));
  765. };
  766. //注销登录;
  767. this.logOff = function () {
  768. var cmdInfo = {};
  769. cmdInfo.action="setAgentStatus";
  770. cmdInfo.body = {"cmd" : "disconnect", "args" : { "cause": "disconnect request from js client." } };
  771. ws.send(JSON.stringify(cmdInfo));
  772. };
  773. /**
  774. * 在咨询失败的情况下使用该按钮,接回处于等待中的电话
  775. */
  776. this.stopCallWaitBtnClickUI = function () {
  777. var cmd = {};
  778. cmd.action="callWait";
  779. cmd.body = {"cmd" : "stop", "args" : {} };
  780. ws.send(JSON.stringify(cmd));
  781. };
  782. /**
  783. * 在咨询成功的情况下使用该按钮,把电话转接给专家坐席。
  784. */
  785. this.transferCallWaitBtnClickUI = function () {
  786. this.callControl("transferCallWait", {})
  787. };
  788. this.consultationBtnClickUI = function () {
  789. let transferType = "outer";
  790. let phoneNumber = $("#externalPhoneNumber").val().trim();
  791. if (phoneNumber === "") {
  792. transferType = "inner";
  793. var groupId = $("#transfer_to_groupIds").val();
  794. if ($.trim(groupId) == "") {
  795. alert("请选择业务组!");
  796. $("#transfer_to_groupIds").focus();
  797. return;
  798. }
  799. var member = $("#transfer_to_member").val();
  800. if ($.trim(member) == "") {
  801. alert("请选择要咨询的坐席成员!");
  802. $("#transfer_to_member").focus();
  803. return;
  804. }
  805. var selectText = $('#transfer_to_member option:selected').text();
  806. if (selectText.indexOf("空闲") == -1) {
  807. alert("请选择空闲的坐席成员!");
  808. $("#transfer_to_member").focus();
  809. return;
  810. }
  811. if (member == this.getOpNum()) {
  812. alert("不能咨询自己,请选择其他坐席成员!");
  813. return;
  814. }
  815. phoneNumber = member;
  816. }
  817. this.callControl("consultation", {"to": phoneNumber, "transferType": transferType});
  818. };
  819. /**
  820. * 处理通话转接按钮点击事件
  821. */
  822. this.transferBtnClickUI = function() {
  823. let transferType = "outer";
  824. let phoneNumber = $("#externalPhoneNumber").val().trim();
  825. if(phoneNumber === "") {
  826. transferType = "inner";
  827. var groupId = $("#transfer_to_groupIds").val();
  828. if ($.trim(groupId) === "") {
  829. alert("请选择转接的业务组!");
  830. $("#transfer_to_groupIds").focus();
  831. return;
  832. }
  833. var member = $("#transfer_to_member").val();
  834. if ($.trim(member) === "") {
  835. alert("请选择转接的坐席成员!");
  836. $("#transfer_to_member").focus();
  837. return;
  838. }
  839. var selectText = $('#transfer_to_member option:selected').text();
  840. if (selectText.indexOf("空闲") === -1) {
  841. alert("请选择空闲的坐席成员!");
  842. $("#transfer_to_member").focus();
  843. return;
  844. }
  845. if (member === this.getOpNum()) {
  846. alert("不能转给自己,请选择其他坐席成员!");
  847. return;
  848. }
  849. phoneNumber = member;
  850. }
  851. this.transferCall(phoneNumber, transferType);
  852. };
  853. /**
  854. * 处理通话转接
  855. * @param userCodeOrPhone 工号或者电话号码
  856. * @param transferType 转接类型:工号还是外部号码(inner or outer)
  857. */
  858. this.transferCall = function(userCodeOrPhone, transferType) {
  859. if(transferType === "inner") {
  860. if (userCodeOrPhone !== this.getOpNum()) {
  861. this.callControl("transferCall", {"to": userCodeOrPhone, "transferType" : "inner" })
  862. } else {
  863. console.error("cant not transfer call to yourself.")
  864. }
  865. }else{
  866. this.callControl("transferCall", {"to": userCodeOrPhone, "transferType" : "outer" })
  867. }
  868. };
  869. //挂机
  870. this.hangup = function() {
  871. this.callControl("endSession", {})
  872. };
  873. // 呼叫控制相关操作;
  874. this.callControl = function(action, argsObject){
  875. var sessionControl = {};
  876. sessionControl.action="call";
  877. sessionControl.body = {"cmd" : action, "args" : argsObject };
  878. ws.send(JSON.stringify(sessionControl));
  879. };
  880. this.checkCallConfirmed = function () {
  881. if(!_cc.getIsConnected()){
  882. console.log('请先上线.');
  883. return false;
  884. }
  885. if(!_cc.getCallConnected()){
  886. console.log('当前没有通话.');
  887. return false;
  888. }
  889. return true;
  890. };
  891. /**
  892. * send and play mp4 video file.
  893. */
  894. this.sendVideoFile = function (mp4FilePath) {
  895. if(!_cc.checkCallConfirmed()){
  896. return false;
  897. }
  898. if(typeof(mp4FilePath) == "undefined" || mp4FilePath.trim().length === 0){
  899. console.log("Parameter mp4FilePath is missing!")
  900. return false;
  901. }
  902. this.callControl(
  903. "playMp4File",
  904. { "mp4FilePath" : mp4FilePath }
  905. );
  906. return true;
  907. };
  908. /**
  909. * 发起视频通话邀请
  910. */
  911. this.reInviteVideoCall = function(){
  912. if(!_cc.checkCallConfirmed()){
  913. return false;
  914. }
  915. if(!_cc.getCanSendVideoReInvite()){
  916. console.log('cant not send video reInvite. ',
  917. 'Precondition is: Call is connected and callType is audio.');
  918. return false;
  919. }
  920. this.callControl(
  921. "reInviteVideo",
  922. {}
  923. );
  924. return true;
  925. };
  926. /**
  927. * 发起外呼
  928. * @param phoneNumber 被叫号码
  929. * @param callType 通话类型:视频通话、音频通话
  930. * @param videoLevel 视频通话的profile-level-id
  931. */
  932. this.call = function(phoneNumber, callType, videoLevel){
  933. if(typeof(videoLevel) == "undefined" || videoLevel.trim().length === 0){
  934. videoLevel = ccPhoneBarSocket.videoLevels.HD.levelId;
  935. console.log("auto default set videoLevel=", videoLevel);
  936. }
  937. if(typeof(callType) == "undefined" || callType.trim().length === 0){
  938. callType = "audio";
  939. console.log("auto default set callType=", callType);
  940. }
  941. console.log("call config videoLevel=" + videoLevel + ", callType=" + callType);
  942. if(phoneNumber==null || phoneNumber.length===0) {
  943. console.log('请输入外呼号码!');
  944. return;
  945. }
  946. if(phoneNumber.trim().length < 3){
  947. alert('请输入正确格式的外呼号码!');
  948. return;
  949. }
  950. if(!_cc.getIsConnected()){
  951. console.log('请先上线.');
  952. return;
  953. }
  954. let outboundInfo = {
  955. "gatewayList": _cc.callConfig.gatewayList,
  956. 'destPhone': phoneNumber,
  957. 'gatewayEncrypted' : _cc.callConfig.gatewayEncrypted,
  958. 'useSameAudioCodeForOutbound' : _cc.callConfig.useSameAudioCodeForOutbound,
  959. 'callType' : callType,
  960. 'videoLevel' : videoLevel
  961. };
  962. this.callControl(
  963. "startSession",
  964. outboundInfo
  965. );
  966. };
  967. this.callEx = function(phoneNumber){
  968. if(phoneNumber == null || phoneNumber.length === 0) {
  969. console.log('请输入外呼号码!');
  970. return;
  971. }
  972. if(!_cc.getIsConnected()){
  973. _cc.connect();
  974. _cc.on(ccPhoneBarSocket.eventList.ws_connected, function(){
  975. _cc.off(ccPhoneBarSocket.eventList.ws_connected); //取消事件订阅
  976. _cc.call(phoneNumber);
  977. });
  978. return;
  979. }
  980. _cc.call(phoneNumber);
  981. };
  982. /************************ 以下是网页工具条ui代码 ************************/
  983. /**
  984. * 根据服务器响应状态码去查找action
  985. * @param code
  986. * @returns {string}
  987. */
  988. ccPhoneBarSocket.findItemByCode = function(code){
  989. for(var item in ccPhoneBarSocket.eventListWithTextInfo ){
  990. if(ccPhoneBarSocket.eventListWithTextInfo[item].code === code){
  991. return ccPhoneBarSocket.eventListWithTextInfo[item];
  992. }
  993. }
  994. };
  995. /**
  996. * 服务器响应状态枚举值;
  997. */
  998. ccPhoneBarSocket.eventListWithTextInfo = {
  999. "ws_connected": { "code": 200, msg:"已签入",
  1000. btn_text:[{id:"#onLineBtn",name:"签出"}],
  1001. enabled_btn:['#setFree','#callBtn','#onLineBtn', '#consultationBtn']
  1002. },
  1003. "ws_disconnected": { "code" : 202, msg:"服务器连接断开",
  1004. btn_text:[{id:"#onLineBtn",name:"签入"}],
  1005. enabled_btn:['#onLineBtn']
  1006. },
  1007. "user_login_on_other_device": { "code" : 201, msg:"用户已在其他设备登录",
  1008. btn_text:[{id:"#onLineBtn",name:"签入"}],
  1009. enabled_btn:['#onLineBtn']
  1010. },
  1011. "request_args_error":{ "code" : 400, msg:"客户端请求参数错误",
  1012. btn_text:[],
  1013. enabled_btn:[]
  1014. },
  1015. "server_error":{ "code" : 500, msg:"服务器内部错误",
  1016. btn_text:[],
  1017. enabled_btn:[]
  1018. },
  1019. "caller_answered":{ "code" : 600, msg:"分机已接通",
  1020. btn_text:[],
  1021. enabled_btn:['#resetStatus', '#hangUpBtn', '#transferBtn', '#holdBtn', '#consultationBtn']
  1022. },
  1023. "lock_agent":{ "code" : 0, msg:"坐席已锁定",
  1024. btn_text:[],
  1025. enabled_btn:['#conferenceBtn']
  1026. },
  1027. "caller_hangup":{ "code" : 601, msg:"分机已挂断",
  1028. btn_text:[],
  1029. enabled_btn:['#onLineBtn', '#resetStatus', '#callBtn', '#setFree', '#consultationBtn' ]
  1030. },
  1031. "caller_busy":{ "code" : 602, msg:"分机忙,上一通电话未挂断",
  1032. btn_text:[],
  1033. enabled_btn:['#onLineBtn', '#resetStatus', '#callBtn', '#setFree', '#consultationBtn']
  1034. },
  1035. "caller_not_login":{ "code" : 603, msg:"分机未登录,请检查",
  1036. btn_text:[],
  1037. enabled_btn:['#onLineBtn', '#resetStatus', '#callBtn', '#setFree', '#consultationBtn']
  1038. },
  1039. "caller_respond_timeout":{ "code" : 604, msg:"分机未应答超时,请重新打开分机",
  1040. btn_text:[],
  1041. enabled_btn:['#onLineBtn', '#resetStatus', '#callBtn', '#setFree', '#consultationBtn']
  1042. },
  1043. "callee_answered":{ "code" : 605, msg:"被叫已接通",
  1044. btn_text:[],
  1045. enabled_btn:['#resetStatus', '#hangUpBtn', '#transferBtn', '#holdBtn', '#consultationBtn' ]
  1046. },
  1047. "callee_hangup":{ "code" : 606, msg:"通话结束",
  1048. btn_text:[],
  1049. enabled_btn:['#onLineBtn', '#resetStatus', '#callBtn', '#setFree' , '#consultationBtn']
  1050. },
  1051. "callee_ringing":{ "code" : 607, msg:"被叫振铃中",
  1052. btn_text:[],
  1053. enabled_btn:['#resetStatus', '#hangUpBtn', '#transferBtn', '#consultationBtn']
  1054. },
  1055. "status_changed":{ "code" : 608, msg:"状态已改变",
  1056. btn_text:[],
  1057. enabled_btn:[ ]
  1058. },
  1059. "free":{ "code" : 0, msg:"空闲中",
  1060. btn_text:[],
  1061. enabled_btn:['#setBusy','#onLineBtn', '#consultationBtn']
  1062. },
  1063. "busy":{ "code" : 1, msg:"忙碌",
  1064. btn_text:[],
  1065. enabled_btn:['#setFree', '#onLineBtn', '#callBtn', '#consultationBtn'] // '#transferBtn'
  1066. },
  1067. "customer_channel_hold" : { "code" : 623, msg:"通话已保持.",
  1068. btn_text:[],
  1069. enabled_btn:['#setFree', '#callBtn', '#unHoldBtn', '#consultationBtn' ]
  1070. },
  1071. "customer_channel_unhold" : { "code" : 624, msg:"通话已接回.",
  1072. btn_text:[],
  1073. enabled_btn:[ '#hangUpBtn', '#holdBtn' ]
  1074. }
  1075. };
  1076. ccPhoneBarSocket.phone_buttons = ['#setFree', '#setBusy', '#callBtn','#hangUpBtn' , '#resetStatus' ,'#onLineBtn', '#transferBtn', '#holdBtn', '#unHoldBtn', '#consultationBtn'];
  1077. // 更新状态显示
  1078. this.updatePhoneBar = function (msg, status_key) {
  1079. if(!_cc.callConfig.useDefaultUi){
  1080. console.log("callConfig.useDefaultUi = false , 已禁用默认ui工具条按钮.");
  1081. return;
  1082. }
  1083. if (msg) {
  1084. $("#callStatus").text(msg.msg);
  1085. }
  1086. var status_info = ccPhoneBarSocket.findItemByCode(status_key);
  1087. if (!status_info) {
  1088. return;
  1089. }
  1090. if(status_info.code === ccPhoneBarSocket.eventListWithTextInfo.status_changed.code){
  1091. if(msg.object.status === ccPhoneBarSocket.agentStatusEnum.free){
  1092. status_info = ccPhoneBarSocket.eventListWithTextInfo.free;
  1093. }else if(msg.object.status === ccPhoneBarSocket.agentStatusEnum.lockStatus){
  1094. status_info = ccPhoneBarSocket.eventListWithTextInfo.lock_agent;
  1095. }else{
  1096. status_info = ccPhoneBarSocket.eventListWithTextInfo.busy;
  1097. }
  1098. }
  1099. // 判断当前是否为状态改变的事件;
  1100. // 显示预设的消息;
  1101. var msgSet = status_info.msg;
  1102. if(msgSet && msgSet.length > 0){
  1103. $("#callStatus").text(msgSet);
  1104. }
  1105. var btn_text = status_info.btn_text;
  1106. var enabled_btn = status_info.enabled_btn;
  1107. if (btn_text) {
  1108. $.each(btn_text, function (i, d) {
  1109. $(d.id).next().text(d.name);
  1110. });
  1111. }
  1112. if (enabled_btn.length === 0) {
  1113. return;
  1114. }
  1115. var all_btn = ccPhoneBarSocket.phone_buttons;
  1116. for (var i in all_btn) {
  1117. var idx = $.inArray(all_btn[i], enabled_btn);
  1118. if (idx < 0) {
  1119. $(all_btn[i]).removeClass('on');
  1120. } else {
  1121. $(enabled_btn[idx]).addClass('on');
  1122. }
  1123. }
  1124. };
  1125. /**
  1126. * 初始化电话工具条ui按钮;
  1127. */
  1128. this.initPhoneBarUI = function () {
  1129. window.onbeforeunload = function () {
  1130. if (!confirm('关闭网页将导致您无法接听电话,确定要关闭吗 ?')) return false;
  1131. };
  1132. $("#unHoldBtnLi").hide();
  1133. if(!_cc.callConfig.useDefaultUi){
  1134. console.log("callConfig.useDefaultUi = false , 已禁用默认ui工具条按钮.");
  1135. return;
  1136. }
  1137. $('#conferenceBtn').on('click', function () {
  1138. if(!_cc.getIsConnected()){
  1139. console.log('请先上线.');
  1140. return;
  1141. }
  1142. var confObjId = document.getElementById("conference_area");
  1143. if(confObjId.style.display === "block"){
  1144. confObjId.style.display = "none";
  1145. }else{
  1146. confObjId.style.display = "block";
  1147. }
  1148. });
  1149. $('#callBtn').on('click', function () {
  1150. if ($(this).hasClass('on')) {
  1151. var destPhone = $.trim($("#ccphoneNumber").val());
  1152. var videoLevel = document.getElementById("videoLevelSelect").value;
  1153. var callType = document.forms[0].callType.value;
  1154. _cc.call(destPhone, callType, videoLevel);
  1155. }
  1156. });
  1157. $('#setFree').on('click', function () {
  1158. if ($(this).hasClass('on')) {
  1159. _cc.setStatus(ccPhoneBarSocket.agentStatusEnum.free);
  1160. }
  1161. });
  1162. $('#setBusy').on('click', function () {
  1163. if ($(this).hasClass('on')) {
  1164. _cc.setStatus(ccPhoneBarSocket.agentStatusEnum.busy);
  1165. }
  1166. });
  1167. $('#setBusySubList').on('change', function () {
  1168. let itemValue = $('#setBusySubList').val();
  1169. console.log('set busy subStatus', itemValue);
  1170. _cc.setStatus(itemValue);
  1171. });
  1172. $('#hangUpBtn').on('click', function () {
  1173. if ($(this).hasClass('on')) {
  1174. _cc.hangup();
  1175. }
  1176. });
  1177. $('#holdBtn').on('click', function () {
  1178. if ($(this).hasClass('on')) {
  1179. _cc.holdCall();
  1180. }
  1181. });
  1182. $('#unHoldBtn').on('click', function () {
  1183. if ($(this).hasClass('on')) {
  1184. _cc.unHoldCall();
  1185. }
  1186. });
  1187. $("#doTransferBtn").hide();
  1188. $('#transferBtn').on('click', function () {
  1189. if ($(this).hasClass('on')) {
  1190. if(!_cc.getIsConnected()){
  1191. console.log('请先上线.');
  1192. return;
  1193. }
  1194. var transferArea = document.getElementById("transfer_area");
  1195. if(transferArea.style.display === "block"){
  1196. transferArea.style.display = "none";
  1197. _phoneBar.unSubscribeAgentList();
  1198. $("#doTransferBtn").hide();
  1199. $("#doConsultationBtn").hide();
  1200. }else{
  1201. transferArea.style.display = "block";
  1202. populateGroupIdOptions();
  1203. _phoneBar.subscribeAgentList();
  1204. $("#doTransferBtn").show();
  1205. $("#doConsultationBtn").hide();
  1206. }
  1207. }
  1208. });
  1209. $("#stopCallWait").hide();
  1210. $("#transferCallWait").hide();
  1211. $("#doConsultationBtn").hide();
  1212. $('#consultationBtn').on('click', function () {
  1213. if ($(this).hasClass('on')) {
  1214. if(!_cc.getIsConnected()){
  1215. console.log('请先上线.');
  1216. return;
  1217. }
  1218. var transferArea = document.getElementById("transfer_area");
  1219. if(transferArea.style.display === "block"){
  1220. transferArea.style.display = "none";
  1221. _phoneBar.unSubscribeAgentList();
  1222. $("#doConsultationBtn").hide();
  1223. $("#doTransferBtn").hide();
  1224. }else{
  1225. transferArea.style.display = "block";
  1226. populateGroupIdOptions();
  1227. _phoneBar.subscribeAgentList();
  1228. $("#doConsultationBtn").show();
  1229. $("#doTransferBtn").hide();
  1230. }
  1231. }
  1232. });
  1233. $('#onLineBtn').on('click', function () {
  1234. if ($(this).hasClass('on')) {
  1235. if (_cc.getIsConnected()) {
  1236. _cc.disconnect();
  1237. } else {
  1238. _cc.connect();
  1239. }
  1240. }else {
  1241. alert('当前不允许签出!');
  1242. }
  1243. });
  1244. $('#resetStatus').on('click', function () {
  1245. window.onbeforeunload = null;
  1246. location.reload();
  1247. });
  1248. //拨号文本框;收到键盘回车事件之后立即拨号
  1249. $("#ccphoneNumber").keydown(function (e) {
  1250. var curKey = e.which;
  1251. if (curKey === 13) {
  1252. var destPhone = $.trim($("#ccphoneNumber").val());
  1253. var videoLevel = document.getElementById("videoLevelSelect").value;
  1254. var callType = document.forms[0].callType.value;
  1255. _cc.call(destPhone,callType, videoLevel);
  1256. return false;
  1257. }
  1258. });
  1259. //ESC按键挂机功能支持
  1260. $(document).keyup(function (e) {
  1261. var key = e.which;
  1262. if (key === 27) {
  1263. console.log('按下了ESC键, 即将发送挂机指令.');
  1264. if(_cc.getIsConnected()){
  1265. if(_cc.callConfig["useDefaultUi"]) {
  1266. if ($('#hangUpBtn').hasClass('on')) {
  1267. _cc.hangup();
  1268. }
  1269. }else{
  1270. _cc.hangup();
  1271. }
  1272. }
  1273. }
  1274. });
  1275. };
  1276. /**
  1277. * 保持通话
  1278. */
  1279. this.holdCall = function(){
  1280. var cmd = {};
  1281. cmd.action="callHold";
  1282. cmd.body = {"cmd" : "hold", "args" : {} };
  1283. ws.send(JSON.stringify(cmd));
  1284. };
  1285. /**
  1286. * 接回保持的通话
  1287. */
  1288. this.unHoldCall = function(){
  1289. var cmd = {};
  1290. cmd.action="callHold";
  1291. cmd.body = {"cmd" : "unhold", "args" : {} };
  1292. ws.send(JSON.stringify(cmd));
  1293. };
  1294. /**
  1295. * 订阅坐席状态列表
  1296. */
  1297. this.subscribeAgentList = function(){
  1298. var cmd = {};
  1299. cmd.action="pollAgentList";
  1300. cmd.body = {"cmd" : "subscribe", "args" : {} };
  1301. ws.send(JSON.stringify(cmd));
  1302. _cc.subscribeAgentListStarted = true;
  1303. };
  1304. /**
  1305. * 取消订阅坐席状态列表
  1306. */
  1307. this.unSubscribeAgentList = function(){
  1308. if(_cc.subscribeAgentListStarted) {
  1309. _cc.subscribeAgentListStarted = false;
  1310. _cc.callConfig.agentList = null;
  1311. var cmd = {};
  1312. cmd.action = "pollAgentList";
  1313. cmd.body = {"cmd": "unSubscribe", "args": {}};
  1314. ws.send(JSON.stringify(cmd));
  1315. }
  1316. };
  1317. /************************* 以下是电话会议相关 ***************************/
  1318. /**
  1319. * 启动会议; 仅限使用默认UI场景下使用;
  1320. */
  1321. this.conferenceStartBtnUI = function(customerName){
  1322. var callType = document.getElementById("conf_call_type").value;
  1323. var confTemplate = document.getElementById("conf_template").value;
  1324. var layOut = document.getElementById("conf_layout").value;
  1325. _cc.setStatusBusy();
  1326. // 禁用外呼按钮
  1327. $("#callBtn").removeClass('on');
  1328. // 禁用置闲按钮
  1329. $("#setFree").removeClass('on');
  1330. // 禁用签出按钮
  1331. $("#onLineBtn").removeClass('on');
  1332. document.getElementById("startConference").setAttribute("disabled", "true");
  1333. if(_cc.getCallConnected()) {
  1334. let tips = "是否把当前通话转换为会议 ?";
  1335. console.log(tips);
  1336. if(confirm(tips)) {
  1337. _cc.transferToConference(layOut, confTemplate, callType, customerName);
  1338. }else{
  1339. document.getElementById("startConference").removeAttribute("disabled");
  1340. }
  1341. }else {
  1342. _cc.conferenceStart(layOut, confTemplate, callType);
  1343. }
  1344. };
  1345. /**
  1346. * 添加会议成员; 仅限使用默认UI场景下使用;
  1347. */
  1348. this.conferenceAddMemberBtnUI = function (reInvite, memberPhoneParam, memberNameParam) {
  1349. var memberName = "";
  1350. var memberPhone = "";
  1351. var memberCallType = $.trim(document.getElementById("member_call_type").value);
  1352. var memberVideoLevel = $.trim(document.getElementById("member_video_level").value);
  1353. if(reInvite === 0) {
  1354. memberName = $("#member_name").val();
  1355. memberPhone = $("#member_phone").val();
  1356. if (memberName.length === 0 || $.trim(memberName) === "") {
  1357. alert('请填写参会者姓名!');
  1358. return;
  1359. }
  1360. if (memberPhone.length === 0 || $.trim(memberPhone) === "") {
  1361. alert('请填写参会者手机号!');
  1362. return;
  1363. }
  1364. memberName = $.trim(memberName);
  1365. memberPhone = $.trim(memberPhone);
  1366. // 使用会员成员html模版添加新成员
  1367. var templateObj = document.getElementById("conf_member_template");
  1368. var existMember = document.getElementById("conf_member_" + memberPhone) != null;
  1369. if (existMember) {
  1370. alert('会议成员已经存在,请不要重复添加!');
  1371. return;
  1372. }
  1373. var memberHtmlItem = templateObj.innerHTML;
  1374. memberHtmlItem = memberHtmlItem.replace(new RegExp("{member_name}", "gm"), memberName);
  1375. memberHtmlItem = memberHtmlItem.replace(new RegExp("{member_phone}", "gm"), memberPhone);
  1376. memberHtmlItem = memberHtmlItem.replace(new RegExp("{member_status}", "gm"), "即将呼叫");
  1377. var li = document.createElement("li");
  1378. li.setAttribute("id", "conf_member_" + memberPhone);
  1379. li.setAttribute("class", "conf_member_item_row");
  1380. li.innerHTML = memberHtmlItem;
  1381. _cc.insertAfter(li, templateObj);
  1382. $("#member_name").val('');
  1383. $("#member_phone").val('');
  1384. }else{
  1385. memberName = memberNameParam;
  1386. memberPhone = memberPhoneParam;
  1387. }
  1388. // 隐藏 mute及 vmute按钮
  1389. var memberItemId = "#conf_member_" + memberPhone;
  1390. $(".conf_mute", $(memberItemId)).find("img").hide();
  1391. $(".conf_vmute", $(memberItemId)).find("img").hide();
  1392. $(".conf_re_invite", $(memberItemId)).hide();
  1393. $(".conf_status", $(memberItemId)).html("即将呼叫");
  1394. _cc.conferenceAddMembers( [
  1395. {"name": memberName, "phone": memberPhone, "callType" : memberCallType, "videoLevel": memberVideoLevel}
  1396. ]);
  1397. };
  1398. /**
  1399. * 从现有通话添加会议成员;
  1400. */
  1401. this.conferenceAddMemberFromExistCall = function (memberName, memberPhone) {
  1402. if (memberName.length === 0 || $.trim(memberName) === "") {
  1403. alert('请填写参会者姓名!');
  1404. return;
  1405. }
  1406. if (memberPhone.length === 0 || $.trim(memberPhone) === "") {
  1407. alert('请填写参会者手机号!');
  1408. return;
  1409. }
  1410. memberName = $.trim(memberName);
  1411. memberPhone = $.trim(memberPhone);
  1412. // 使用html模版添加新成员
  1413. var templateObj = document.getElementById("conf_member_template");
  1414. var existMember = document.getElementById("conf_member_" + memberPhone) != null;
  1415. if (existMember) {
  1416. alert('会议成员已经存在,请不要重复添加!');
  1417. return;
  1418. }
  1419. var memberHtmlItem = templateObj.innerHTML;
  1420. memberHtmlItem = memberHtmlItem.replace(new RegExp("{member_name}", "gm"), memberName);
  1421. memberHtmlItem = memberHtmlItem.replace(new RegExp("{member_phone}", "gm"), memberPhone);
  1422. memberHtmlItem = memberHtmlItem.replace(new RegExp("{member_status}", "gm"), "即将呼叫");
  1423. var li = document.createElement("li");
  1424. li.setAttribute("id", "conf_member_" + memberPhone);
  1425. li.setAttribute("class", "conf_member_item_row");
  1426. li.innerHTML = memberHtmlItem;
  1427. _cc.insertAfter(li, templateObj);
  1428. // 隐藏 mute及 vmute按钮
  1429. var memberItemId = "#conf_member_" + memberPhone;
  1430. $(".conf_mute", $(memberItemId)).find("img").show();
  1431. $(".conf_vmute", $(memberItemId)).find("img").show();
  1432. $(".conf_re_invite", $(memberItemId)).hide();
  1433. $(".conf_status", $(memberItemId)).html("通话中").css("color", "green");
  1434. };
  1435. /**
  1436. * 通话转为会议
  1437. * @param layOut
  1438. * @param confTemplate
  1439. * @param callType
  1440. * @param customerName
  1441. */
  1442. this.transferToConference = function (layOut, confTemplate, callType, customerName) {
  1443. console.log("try to transferToConference: ", layOut, confTemplate, callType, customerName);
  1444. _cc.callControl(
  1445. "transferToConference",
  1446. {
  1447. "layOut": layOut,
  1448. 'callType': callType,
  1449. 'confTemplate': confTemplate,
  1450. 'customerName' : customerName
  1451. }
  1452. );
  1453. };
  1454. /**
  1455. * 主持人启动电话会议
  1456. * @param layOut 会议布局
  1457. * @param confTemplate 会议模版
  1458. * @param callType 会议类型
  1459. */
  1460. this.conferenceStart = function(layOut, confTemplate, callType) {
  1461. console.log("正常发起多方通话");
  1462. var cmd = {};
  1463. cmd.action = "conference";
  1464. cmd.body = {"method": "startconf", "args": {
  1465. "layOut": layOut,
  1466. 'callType': callType,
  1467. 'confTemplate': confTemplate
  1468. }};
  1469. ws.send(JSON.stringify(cmd));
  1470. };
  1471. /**
  1472. * VMute会议成员(不显示视频);
  1473. **/
  1474. this.conferenceVMuteMember = function(members) {
  1475. // single phone
  1476. if(typeof(members) === "string") {
  1477. this.conferenceControl("vmute",
  1478. [
  1479. {"phone": members}
  1480. ]
  1481. );
  1482. }else{
  1483. // multiple phones array, e.g.: [ {"phone": '15005600321'}, {"phone": '15005600323'} ]
  1484. this.conferenceControl("vmute", members);
  1485. }
  1486. };
  1487. /**
  1488. * UnVMute会议成员(显示视频);
  1489. **/
  1490. this.conferenceUnVMuteMember = function(members) {
  1491. // single phone
  1492. if(typeof(members) === "string") {
  1493. this.conferenceControl("unvmute",
  1494. [
  1495. {"phone": members}
  1496. ]
  1497. );
  1498. }else{
  1499. // multiple phones array, e.g.: [ {"phone": '15005600321'}, {"phone": '15005600323'} ]
  1500. this.conferenceControl("unvmute", members);
  1501. }
  1502. };
  1503. /**
  1504. * 禁言会议成员
  1505. **/
  1506. this.conferenceMuteMember = function(members) {
  1507. // single phone
  1508. if(typeof(members) === "string") {
  1509. this.conferenceControl("mute",
  1510. [
  1511. {"phone": members}
  1512. ]
  1513. );
  1514. }else{
  1515. // multiple phones array, e.g.: [ {"phone": '15005600321'}, {"phone": '15005600323'} ]
  1516. this.conferenceControl("mute", members);
  1517. }
  1518. };
  1519. /**
  1520. * 解禁会议成员
  1521. **/
  1522. this.conferenceUnMuteMember = function(members) {
  1523. // single phone
  1524. if(typeof(members) === "string") {
  1525. this.conferenceControl("unmute",
  1526. [
  1527. {"phone": members}
  1528. ]
  1529. );
  1530. }else{
  1531. // multiple phones array, e.g.: [ {"phone": '15005600321'}, {"phone": '15005600323'} ]
  1532. this.conferenceControl("unmute", members);
  1533. }
  1534. };
  1535. /**
  1536. * 增加会议成员
  1537. **/
  1538. this.conferenceAddMembers = function(members) {
  1539. this.conferenceControl("add", members)
  1540. };
  1541. /**
  1542. * 移除会议成员
  1543. **/
  1544. this.conferenceRemoveMembers = function(members) {
  1545. // single phone
  1546. if(typeof(members) === "string") {
  1547. var memberItemObj = $("#conf_member_" + members);
  1548. if($(".conf_status", memberItemObj).text() === "通话中") {
  1549. this.conferenceControl("remove",
  1550. [
  1551. {"phone": members}
  1552. ]
  1553. );
  1554. }
  1555. memberItemObj.remove();
  1556. }else{
  1557. // multiple phones array, e.g.: [ {"phone": '15005600321'}, {"phone": '15005600323'} ]
  1558. this.conferenceControl("remove", members);
  1559. }
  1560. };
  1561. /**
  1562. * 结束电话会议
  1563. **/
  1564. this.conferenceEnd = function() {
  1565. this.conferenceControl("endconf", [])
  1566. };
  1567. /**
  1568. * 电话会议控制相关操作
  1569. * @param action 操作
  1570. * @param phoneList 会议成员
  1571. */
  1572. this.conferenceControl = function (action, phoneList) {
  1573. var cmd = {};
  1574. cmd.action = "conference";
  1575. cmd.body = { "method": action, "memberList": phoneList };
  1576. ws.send(JSON.stringify(cmd));
  1577. };
  1578. /************************* 以下是通话监听相关 ***************************/
  1579. /**
  1580. * 拉取监听的通话列表
  1581. */
  1582. this.callMonitorDataPull = function (){
  1583. var cmd = {};
  1584. cmd.action = "monitorData";
  1585. cmd.body = {};
  1586. ws.send(JSON.stringify(cmd));
  1587. };
  1588. /**
  1589. * 拉取排队中的电话列表
  1590. */
  1591. this.inboundCallQueuePull = function (){
  1592. var cmd = {};
  1593. cmd.action = "inboundMonitorData";
  1594. cmd.body = {};
  1595. ws.send(JSON.stringify(cmd));
  1596. };
  1597. // 构造通话监听参数
  1598. this.monitorControl = function(action, argsObject){
  1599. var sessionControl = {};
  1600. sessionControl.action="callMonitor";
  1601. sessionControl.body = {"cmd" : action, "args" : argsObject };
  1602. ws.send(JSON.stringify(sessionControl));
  1603. };
  1604. /**
  1605. * 通话监听
  1606. * @param { 通话id } callId
  1607. * @returns
  1608. */
  1609. this.callMonitorStart = function(callId){
  1610. if(callId == null || callId.length === 0) {
  1611. console.log('请提供待监听电话的 callId !');
  1612. return;
  1613. }
  1614. if(!_cc.getIsConnected()){
  1615. console.log('请先上线.');
  1616. return;
  1617. }
  1618. this.monitorControl(
  1619. "startMonitoring",
  1620. {
  1621. 'callSpyId': callId
  1622. }
  1623. );
  1624. };
  1625. /**
  1626. * 结束监听
  1627. */
  1628. this.callMonitorEnd = function(){
  1629. if(!_cc.getIsConnected()){
  1630. console.log('请先上线.');
  1631. return;
  1632. }
  1633. this.monitorControl(
  1634. "endMonitoring",{}
  1635. );
  1636. };
  1637. }