phone-bar-ex.html 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <title>呼叫中心html客户端工具条</title>
  5. <meta charset="UTF-8" />
  6. <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  7. <link rel="stylesheet" href="page.css" />
  8. <script type="text/javascript" src="jquery-1.11.0.js"></script>
  9. <script type="text/javascript" src="ccPhoneBarSocket.js"></script>
  10. <script type="text/javascript">
  11. var _phoneBar = new ccPhoneBarSocket();
  12. var scriptServer = "192.168.67.217";
  13. var extnum = '1001'; //分机号
  14. var opnum = '1001'; //工号
  15. var skillLevel = 9; //技能等级
  16. var groupId = 1; // 业务组id
  17. if(window.location.href.toString().indexOf("?") != -1){
  18. console.log( ccPhoneBarSocket.utils );
  19. extnum = ccPhoneBarSocket.utils.getQueryParam("extNum");
  20. opnum = ccPhoneBarSocket.utils.getQueryParam("opNum");
  21. groupId = ccPhoneBarSocket.utils.getQueryParam("groupId");
  22. console.log("extNum=", extnum, "opNum=", opnum);
  23. }
  24. function resetExtNumAndOpNum(ext, op, groupId) {
  25. window.location.href = "?extNum=" + ext + "&opNum=" + op + "&groupId=" + groupId;
  26. };
  27. (function loadLoginToken(){
  28. var getTokenUrl = "http://"+ scriptServer +":8880/call-center/create-token";
  29. var destUrl = getTokenUrl + "?extnum=" + extnum + "&opnum=" + opnum
  30. + "&groupId=" + groupId +"&skillLevel=" + skillLevel
  31. ;
  32. var script = document.createElement("script");
  33. script.type = "text/javascript";
  34. script.src = destUrl;
  35. document.getElementsByTagName('head')[0].appendChild(script);
  36. })();
  37. (function loadExtPassword(){
  38. var extPassword = '1234567';
  39. var url = "http://"+ scriptServer +":8880/call-center/create-ext-password?pass=" + extPassword;
  40. var script = document.createElement("script");
  41. script.type = "text/javascript";
  42. script.src = url;
  43. document.getElementsByTagName('head')[0].appendChild(script);
  44. })();
  45. (function loadGatewayList(){
  46. var url = "http://"+ scriptServer +":8880/call-center/create-gateway-list" ;
  47. var script = document.createElement("script");
  48. script.type = "text/javascript";
  49. script.src = url;
  50. document.getElementsByTagName('head')[0].appendChild(script);
  51. })();
  52. // 将视频级别填充到下拉列表中的函数
  53. function populateVideoLevelDropdown(objId) {
  54. let select = document.getElementById(objId);
  55. if(select == null) return;
  56. // 遍历视频级别数据
  57. for (let key in ccPhoneBarSocket.videoLevels) {
  58. if (ccPhoneBarSocket.videoLevels.hasOwnProperty(key)) {
  59. let level = ccPhoneBarSocket.videoLevels[key];
  60. let option = document.createElement('option');
  61. option.value = level.levelId; // 设置值为 levelId
  62. option.text = level.description; // 显示文本
  63. select.appendChild(option);
  64. }
  65. }
  66. select.value = ccPhoneBarSocket.videoLevels.HD.levelId ;
  67. }
  68. </script>
  69. <script>
  70. var _callConfig = null;
  71. window.onload = function(){
  72. // 调用函数填充视频清晰度的下拉列表
  73. populateVideoLevelDropdown('videoLevelSelect');
  74. populateVideoLevelDropdown('member_video_level');
  75. //工具条对象断开事件
  76. // _phoneBar.on(ccPhoneBarSocket.eventList.ws_disconnected, function(msg){
  77. // console.log(msg);
  78. // });
  79. //
  80. // //工具条对象连接成功
  81. // _phoneBar.on(ccPhoneBarSocket.eventList.ws_connected, function(msg){
  82. // console.log(msg);
  83. // });
  84. //
  85. // _phoneBar.on(ccPhoneBarSocket.eventList.callee_ringing, function(msg){
  86. // console.log(msg.content, "被叫振铃事件");
  87. // });
  88. // _phoneBar.on(ccPhoneBarSocket.eventList.caller_answered, function(msg){
  89. // console.log(msg, "主叫接通" );
  90. // });
  91. // _phoneBar.on(ccPhoneBarSocket.eventList.caller_hangup, function(msg){
  92. // console.log(msg, "主叫挂断");
  93. // });
  94. //
  95. // _phoneBar.on(ccPhoneBarSocket.eventList.callee_answered, function(msg){
  96. // console.log(msg, "被叫接通");
  97. // });
  98. // _phoneBar.on(ccPhoneBarSocket.eventList.callee_hangup, function(msg){
  99. // console.log(msg, "被叫挂断");
  100. // });
  101. //
  102. // _phoneBar.on(ccPhoneBarSocket.eventList.status_changed, function(msg){
  103. // console.log("座席状态改变: " ,msg);
  104. // });
  105. //
  106. // // 一次外呼结束;
  107. // _phoneBar.on(ccPhoneBarSocket.eventList.outbound_finished, function(msg){
  108. // console.log('一次外呼结束', msg);
  109. // });
  110. // websocket通信对象断开事件;
  111. _phoneBar.on(ccPhoneBarSocket.eventListWithTextInfo.ws_disconnected.code, function(msg){
  112. console.log(msg);
  113. _phoneBar.updatePhoneBar(msg, ccPhoneBarSocket.eventListWithTextInfo.ws_disconnected.code);
  114. $("#transfer_area").hide();
  115. });
  116. _phoneBar.on(ccPhoneBarSocket.eventList.extension_cannot_connected, function(msg){
  117. console.log('extension cannot connected', msg);
  118. });
  119. _phoneBar.on(ccPhoneBarSocket.eventList.OUTBOUND_START, function (msg) {
  120. console.log('outbound_start',msg);
  121. });
  122. _phoneBar.on(ccPhoneBarSocket.eventListWithTextInfo.request_args_error.code, function(msg){
  123. console.log(msg);
  124. _phoneBar.updatePhoneBar(msg, ccPhoneBarSocket.eventListWithTextInfo.request_args_error.code);
  125. });
  126. //用户已在其他设备登录
  127. _phoneBar.on(ccPhoneBarSocket.eventListWithTextInfo.user_login_on_other_device.code, function(msg){
  128. _phoneBar.updatePhoneBar(msg, ccPhoneBarSocket.eventListWithTextInfo.user_login_on_other_device.code);
  129. alert(ccPhoneBarSocket.eventListWithTextInfo.user_login_on_other_device.msg);
  130. });
  131. //websocket连接成功
  132. _phoneBar.on(ccPhoneBarSocket.eventListWithTextInfo.ws_connected.code, function(msg){
  133. console.log(msg);
  134. $("#loginTime").text(new Date().toLocaleTimeString());
  135. _phoneBar.updatePhoneBar(msg, ccPhoneBarSocket.eventListWithTextInfo.ws_connected.code);
  136. });
  137. _phoneBar.on(ccPhoneBarSocket.eventListWithTextInfo.callee_ringing.code, function(msg){
  138. console.log(msg.content, "被叫振铃事件");
  139. _phoneBar.updatePhoneBar(msg, ccPhoneBarSocket.eventListWithTextInfo.callee_ringing.code);
  140. });
  141. _phoneBar.on(ccPhoneBarSocket.eventListWithTextInfo.caller_answered.code, function(msg){
  142. console.log(msg, "主叫接通" );
  143. $("#agentStatus").text("通话中");
  144. _phoneBar.updatePhoneBar(msg, ccPhoneBarSocket.eventListWithTextInfo.caller_answered.code);
  145. });
  146. _phoneBar.on(ccPhoneBarSocket.eventListWithTextInfo.caller_hangup.code, function(msg){
  147. console.log(msg, "主叫挂断");
  148. $("#agentStatus").text("通话结束");
  149. $("#reInviteVideoBtn").attr("disabled","disabled");
  150. $("#sendVideoFileBtn").attr("disabled","disabled");
  151. $("#transfer_area").hide();
  152. _phoneBar.updatePhoneBar(msg, ccPhoneBarSocket.eventListWithTextInfo.caller_hangup.code);
  153. });
  154. _phoneBar.on(ccPhoneBarSocket.eventListWithTextInfo.callee_answered.code, function(msg){
  155. console.log(msg, "被叫接通");
  156. _phoneBar.updatePhoneBar(msg, ccPhoneBarSocket.eventListWithTextInfo.callee_answered.code);
  157. });
  158. _phoneBar.on(ccPhoneBarSocket.eventListWithTextInfo.callee_hangup.code, function(msg){
  159. console.log(msg, "被叫挂断");
  160. $("#transfer_area").hide();
  161. _phoneBar.updatePhoneBar(msg, ccPhoneBarSocket.eventListWithTextInfo.callee_hangup.code);
  162. });
  163. _phoneBar.on(ccPhoneBarSocket.eventListWithTextInfo.status_changed.code, function(msg){
  164. console.log("座席状态改变: " ,msg);
  165. $("#agentStatus").text(msg["object"]["text"]);
  166. _phoneBar.updatePhoneBar(msg, ccPhoneBarSocket.eventListWithTextInfo.status_changed.code);
  167. });
  168. _phoneBar.on(ccPhoneBarSocket.eventList.acd_group_queue_number, function(msg){
  169. console.log("当前排队人数消息: " ,msg);
  170. $("#queueStat").text(msg["object"]["queue_number"]);
  171. });
  172. _phoneBar.on(ccPhoneBarSocket.eventList.on_audio_call_connected, function(msg){
  173. console.log("音频通话已建立: " ,msg);
  174. $("#reInviteVideoBtn").removeAttr("disabled");
  175. });
  176. _phoneBar.on(ccPhoneBarSocket.eventList.customer_channel_hold, function (msg) {
  177. console.log("客户通话已保持: " ,msg);
  178. $("#callStatus").text("通话已保持");
  179. });
  180. _phoneBar.on(ccPhoneBarSocket.eventList.customer_channel_unhold, function (msg) {
  181. console.log("客户通话已接回." ,msg);
  182. $("#callStatus").text("客户通话已接回");
  183. });
  184. _phoneBar.on(ccPhoneBarSocket.eventList.on_video_call_connected, function(msg){
  185. console.log("视频通话已建立: " ,msg);
  186. $("#sendVideoFileBtn").removeAttr("disabled");
  187. });
  188. _phoneBar.on(ccPhoneBarSocket.eventList.inner_consultation_start, function(msg){
  189. $("#callStatus").text("咨询开始.");
  190. });
  191. _phoneBar.on(ccPhoneBarSocket.eventList.inner_consultation_stop, function(msg){
  192. $("#callStatus").text("咨询结束.");
  193. });
  194. _phoneBar.on(ccPhoneBarSocket.eventList.transfer_call_success, function(msg){
  195. $("#callStatus").text("电话转接成功.");
  196. $("#externalPhoneNumber").val('');
  197. });
  198. // 订阅的坐席状态列表发生改变
  199. _phoneBar.on(ccPhoneBarSocket.eventList.agent_status_data_changed, function (msg) {
  200. console.log("agent_status_data_changed.");
  201. // 当 transfer_to_groupId 值改变时更新 transfer_to_member
  202. $(transferToGroupId).off("change");
  203. $(transferToGroupId).on("change", function () {
  204. refreshMemberIdList();
  205. });
  206. refreshMemberIdList();
  207. });
  208. /* conference related events */
  209. _phoneBar.on(ccPhoneBarSocket.eventList.CONFERENCE_MEMBER_ANSWERED, function (msg) {
  210. console.log("会议成员已经接通.", msg);
  211. var memberPhone = $.trim(msg.object.phone);
  212. var memberItemId = "#conf_member_" + memberPhone;
  213. $(".conf_status", $(memberItemId)).text(msg.object.status);
  214. $(".conf_status", $(memberItemId)).html("通话中").css("color", "green");
  215. $(".conf_mute", $(memberItemId)).find("img").show();
  216. $(".conf_vmute", $(memberItemId)).find("img").show();
  217. });
  218. _phoneBar.on(ccPhoneBarSocket.eventList.CONFERENCE_MEMBER_VMUTED_SUCCESS, function (msg) {
  219. console.log("会议成员已被禁用视频.", msg);
  220. var memberPhone = $.trim(msg.object.phone);
  221. var muteObj = $(".conf_vmute", $("#conf_member_" + memberPhone));
  222. muteObj.find("img")[0].src = "images/no_video.jpg";
  223. muteObj.find("a").removeAttr("onclick");
  224. muteObj.find("a").off("click");
  225. muteObj.find("a").on("click", function () {
  226. _phoneBar.conferenceUnVMuteMember(memberPhone);
  227. } );
  228. });
  229. _phoneBar.on(ccPhoneBarSocket.eventList.CONFERENCE_MEMBER_UnVMUTED_SUCCESS, function (msg) {
  230. console.log("会议成员启用视频成功.", msg);
  231. var memberPhone = $.trim(msg.object.phone);
  232. var muteObj = $(".conf_vmute", $("#conf_member_" + memberPhone));
  233. muteObj.find("img")[0].src = "images/video.jpg";
  234. muteObj.find("a").removeAttr("onclick");
  235. muteObj.find("a").off("click");
  236. muteObj.find("a").on("click", function () {
  237. _phoneBar.conferenceVMuteMember(memberPhone);
  238. } );
  239. });
  240. _phoneBar.on(ccPhoneBarSocket.eventList.CONFERENCE_MEMBER_MUTED_SUCCESS, function (msg) {
  241. console.log("会议成员已被禁言.", msg);
  242. var memberPhone = $.trim(msg.object.phone);
  243. var muteObj = $(".conf_mute", $("#conf_member_" + memberPhone));
  244. muteObj.find("img")[0].src = "images/unmute.jpg";
  245. muteObj.find("a").removeAttr("onclick");
  246. muteObj.find("a").off("click");
  247. muteObj.find("a").on("click", function () {
  248. _phoneBar.conferenceUnMuteMember(memberPhone);
  249. } );
  250. });
  251. _phoneBar.on(ccPhoneBarSocket.eventList.CONFERENCE_MEMBER_UNMUTED_SUCCESS, function (msg) {
  252. console.log("会议成员解除禁言成功.", msg);
  253. var memberPhone = $.trim(msg.object.phone);
  254. var muteObj = $(".conf_mute", $("#conf_member_" + memberPhone));
  255. muteObj.find("img")[0].src = "images/mute.jpg";
  256. muteObj.find("a").removeAttr("onclick");
  257. muteObj.find("a").off("click");
  258. muteObj.find("a").on("click", function () {
  259. _phoneBar.conferenceMuteMember(memberPhone);
  260. } );
  261. });
  262. _phoneBar.on(ccPhoneBarSocket.eventList.CONFERENCE_MEMBER_HANGUP, function (msg) {
  263. console.log("会议成员已经挂机.", msg);
  264. var memberPhone = $.trim(msg.object.phone);
  265. var memberItemId = "#conf_member_" + memberPhone;
  266. // 隐藏 mute及 vmute按钮
  267. $(".conf_mute", $(memberItemId)).find("img").hide();
  268. $(".conf_vmute", $(memberItemId)).find("img").hide();
  269. $(".conf_re_invite", $(memberItemId)).show();
  270. var answerStatus = ( msg.object.answeredTime === 0) ? "未接通" : msg.object.hangupClause;
  271. var color = ( msg.object.answeredTime === 0) ? "red" : "green";
  272. $(".conf_status", $(memberItemId)).html("已挂机("+ answerStatus +")").css("color", color);
  273. $(".conf_status", $(memberItemId)).fadeTo('fast', 0.1).fadeTo('fast', 1.0);
  274. var blinkText = setInterval(function () {
  275. $(".conf_status", $(memberItemId)).fadeTo('fast', 0.1).fadeTo('fast', 1.0);
  276. }, 700); // 每0.5秒闪烁一次
  277. setTimeout(function () {
  278. console.log("memberItemId=", memberItemId);
  279. clearInterval(blinkText);
  280. // $(memberItemId).remove(); //暂不自动移除参会者,由主持人手动操作处理;
  281. }, 5000);
  282. });
  283. _phoneBar.on(ccPhoneBarSocket.eventList.CONFERENCE_MODERATOR_ANSWERED, function (msg) {
  284. console.log("电话会议开始,主持人已接通.", msg);
  285. onConferenceStart();
  286. });
  287. _phoneBar.on(ccPhoneBarSocket.eventList.CONFERENCE_MODERATOR_HANGUP, function (msg) {
  288. console.log("电话会议结束,主持人已挂机.", msg);
  289. onConferenceEnd();
  290. });
  291. _phoneBar.on(ccPhoneBarSocket.eventList.CONFERENCE_TRANSFER_SUCCESS_FROM_EXISTED_CALL, function (msg) {
  292. console.log("成功把通话转接到多人视频会议.", msg);
  293. onTransferToConferenceSuccess(msg);
  294. });
  295. var _gatewayList = [
  296. {
  297. "gatewayAddr":"192.168.67.217:5080",
  298. "callProfile":"internal",
  299. "authUsername":"1002",
  300. "callerNumber":"13195510173\r\n13195510174\r\n13195510188",
  301. "updateTime":1769767068989,
  302. "calleePrefix":"",
  303. "priority":1,
  304. "audioCodec":"pcma",
  305. "uuid":"3",
  306. "concurrency":2,
  307. "register":0
  308. }
  309. ];
  310. var _gatewayList2 = [
  311. {"gatewayAddr":"192.168.31.252:111","callProfile":"external","callerNumber":"1004","updateTime":1772073618994,
  312. "calleePrefix":"","priority":1,"audioCodec":"pcma","uuid":"2","concurrency":2,"register":0
  313. },
  314. {"gatewayAddr":"192.168.67.217:5080","callProfile":"internal","callerNumber":"13195510173\r\n13195510174\r\n13195510188",
  315. "updateTime":1769767068989,"calleePrefix":"","priority":1,"audioCodec":"pcma","uuid":"3","concurrency":2,"register":0
  316. },
  317. {"gatewayAddr":"192.168.31.59:63406","callProfile":"external","callerNumber":"1004","updateTime":1772585206484,
  318. "calleePrefix":"","priority":1,"audioCodec":"pcma","uuid":"4","concurrency":2,"register":0
  319. },
  320. {"gatewayAddr":"192.168.31.64:54354","callProfile":"external","callerNumber":"1005","updateTime":1771991512294,
  321. "calleePrefix":"","priority":1,"audioCodec":"pcma","uuid":"5","concurrency":2,"register":0
  322. },
  323. {"gatewayAddr":"192.168.31.167:49424","callProfile":"external","callerNumber":"1007","updateTime":1770710040756,
  324. "calleePrefix":"","priority":1,"audioCodec":"pcma","uuid":"6","concurrency":2,"register":0
  325. }
  326. ];
  327. // 电话工具条参数配置;
  328. _callConfig = {
  329. 'useDefaultUi' : true,
  330. // loginToken 信息是加密的字符串, 包含以下字段信息:extnum[分机号]、opnum[工号]、groupId[业务组]、skillLevel[技能等级]
  331. 'loginToken': '',
  332. // 电话工具条服务器端的地址; 端口默认是1081
  333. 'ipccServer': scriptServer + ':1081',
  334. // 网关列表, 默认需要加密后在在通过客户端向呼叫系统传递;
  335. // 注意在注册模式下,网关参数更改之后,必须重启语音服务 [docker restart freeswitch] 方可生效,不支持热更新;
  336. // 支持多个网关同时使用,按照优先级依次使用, 支持网关负载容错溢出 [第一条网关外呼出错后,自动使用第二条网关重试,直至外呼不出错] ;
  337. 'gatewayList': _gatewayList,
  338. // 网关列表信息是否为加密模式;
  339. 'gatewayEncrypted': false
  340. };
  341. // 使用工具条之前需要先初始化 _callConfig 参数, 填充各个字段的值: 合计7个字段,必须填写正确 ;
  342. //********************************************************************************************
  343. // 以下代码设置加密的参数: loginToken、extPassword、gatewayList; 在本页面的demo演示中需要调用服务器端接口获取密文字符串;
  344. console.log('loginToken = ',loginToken);
  345. if(typeof(loginToken) != "undefined") {
  346. _callConfig["loginToken"] = loginToken;
  347. } else{
  348. alert("电话工具条:无法获取 loginToken!");
  349. return;
  350. }
  351. console.log('_phoneEncryptPassword = ',_phoneEncryptPassword);
  352. if(typeof(_phoneEncryptPassword) != "undefined") {
  353. _callConfig["extPassword"] = _phoneEncryptPassword;
  354. } else{
  355. alert("电话工具条:无法获取 _phoneEncryptPassword!");
  356. return;
  357. }
  358. console.log('_configGatewayList = ', _configGatewayList);
  359. if(typeof(_configGatewayList) != "undefined" && _callConfig["gatewayEncrypted"]) {
  360. //_callConfig["gatewayList"] = _configGatewayList;
  361. } else{
  362. // alert("电话工具条:无法获取 _configGatewayList!");
  363. }
  364. _phoneBar.initConfig(_callConfig);
  365. };
  366. function onConferenceEnd() {
  367. document.getElementById("endConference").setAttribute("disabled", "true");
  368. document.getElementById("startConference").removeAttribute("disabled");
  369. document.getElementById("conference_member_list").style.display = "none";
  370. // 启用外呼按钮
  371. $("#callBtn").addClass('on');
  372. // 启用置闲按钮
  373. $("#setFree").addClass('on');
  374. // 启用签出按钮
  375. $("#onLineBtn").addClass('on');
  376. //移除所有的参会成员
  377. $(".conf_member_item_row").remove();
  378. let tips = "多方通话结束";
  379. $("#callStatus").text(tips);
  380. $("#agentStatus").text(tips);
  381. }
  382. function onConferenceStart() {
  383. document.getElementById("endConference").removeAttribute("disabled");
  384. document.getElementById("conference_member_list").style.display = "block";
  385. let tips = "多方通话进行中";
  386. $("#callStatus").text(tips);
  387. $("#agentStatus").text(tips);
  388. }
  389. /**
  390. * 成功把电话转接到多人视频会议
  391. */
  392. function onTransferToConferenceSuccess(msg) {
  393. $("#callStatus").text("已接入多方会议");
  394. $("#setFree").removeClass("on");
  395. $("#setBusy").removeClass("on");
  396. $("#callBtn").removeClass("on");
  397. //界面显示成功转接到视频会议电话
  398. var phone = msg.object.phone;
  399. var name = msg.object.phone;
  400. console.log("onTransferToConferenceSuccess:", msg);
  401. _phoneBar.conferenceAddMemberFromExistCall(name, phone);
  402. }
  403. </script>
  404. </head>
  405. <body>
  406. <form>
  407. <table width="1224">
  408. <tr>
  409. <td width="70%" colspan="2" height="35" style="text-indent: 20px;">
  410. <b>签入时间:</b> <span id="loginTime" title="" class="status4">00:00:00</span> &nbsp;&nbsp;
  411. <b>状态:</b> <span id="agentStatus" title="" class="status4">空闲</span> &nbsp;&nbsp;
  412. <b>当前排队人数:</b><span id="queueStat" title="" class="status4">0</span>
  413. </td>
  414. </tr>
  415. <tr>
  416. <td width="70%">
  417. <div>
  418. <div class="head_dial" style="padding-left: 10px; ">
  419. <dl class="dial">
  420. <dt>
  421. <label for="ccphoneNumber"></label><input type="text" name="ccphoneNumber" id="ccphoneNumber" placeholder="输入电话号码" class="tel_txt" />
  422. </dt>
  423. <dd>
  424. <ul><li id="callStatus" title="" class="status4">没有连接</li></ul>
  425. <span id="showCallLen" style="display:none"><b>00:00</b></span>
  426. </dd>
  427. </dl>
  428. <ul class="dial_btn">
  429. <li><a href="#" id="setFree" class="xz_btn off"></a><span>置闲</span></li>
  430. <li><a href="#" id="setBusy" class="sm_btn off"></a>
  431. <select style="width: 50px;" id="setBusySubList">
  432. <option value="3">置忙</option>
  433. <option value="31">小休</option>
  434. <option value="32">会议</option>
  435. <option value="33">培训</option>
  436. </select>
  437. </li>
  438. <li><a href="#" id="callBtn" class="wh_btn"></a><span>外呼</span></li>
  439. <li id="holdBtnLi"><a href="#" id="holdBtn" class="bc_btn off"></a><span>保 持</span></li>
  440. <li id="unHoldBtnLi"><a href="#" id="unHoldBtn" class="bc2_btn off"></a><span>取消保持</span></li>
  441. <li><a href="#" id="transferBtn" class="zjie_btn"></a><span>转接</span></li>
  442. <li><a href="#" id="consultationBtn" class="zixun_btn"></a><span>咨询</span></li>
  443. <li><a href="#" id="conferenceBtn" class="hy_btn"></a><span>会议</span></li>
  444. <li><a href="#" id="hangUpBtn" class="gj_btn"></a><span>挂机</span></li>
  445. <li><a href="#" id="resetStatus" class="qz_btn"></a><span>强置</span></li>
  446. <li><a href="#" id="onLineBtn" class="sx_btn on"></a><span>签入</span></li>
  447. </ul>
  448. </div>
  449. </div>
  450. </td>
  451. <td width="30%" style="display: none;">
  452. <div>
  453. <div style="padding-left: 10px; ">
  454. &nbsp; &nbsp; 外呼设置:
  455. <label for="videoCallBtn"> <input type="radio" value="video" name="callType" id="videoCallBtn" />视频外呼</label> &nbsp;&nbsp;
  456. <label for="audioCallBtn"> <input type="radio" value="audio" name="callType" checked="checked" id="audioCallBtn" />语音外呼</label> <br />
  457. &nbsp; &nbsp; 视频清晰度:
  458. <label for="videoLevelSelect"></label><select id="videoLevelSelect">
  459. </select>
  460. <input type="button" id="reInviteVideoBtn" title="发送视频邀请,可把音频通话转换为视频通话。"
  461. onclick="_phoneBar.reInviteVideoCall();" value="视频邀请" disabled="disabled" >
  462. &nbsp;&nbsp;&nbsp;&nbsp;
  463. <label for="videoListSelect"></label>
  464. <select id="videoListSelect">
  465. <option value="">请选择视频</option>
  466. <option value="/usr/local/freeswitchvideo/share/freeswitch/sounds/bank.mp4">客服实例视频</option>
  467. <option value="/usr/local/freeswitchvideo/share/freeswitch/sounds/conference.mp4">多方会议视频</option>
  468. <option value="/usr/local/freeswitchvideo/share/freeswitch/sounds/15-seconds.mp4">15-seconds-demo</option>
  469. </select>
  470. <input type="button" id="sendVideoFileBtn" title="推送视频给对方,以便结束当前通话。"
  471. onclick="_phoneBar.sendVideoFile($('#videoListSelect').val());" value="推送视频" disabled="disabled" >
  472. </div>
  473. </div>
  474. </td>
  475. </tr>
  476. <tr id="conference_area" style="display: none">
  477. <td colspan="2" style="padding-left: 130px; padding-top: 30px;">
  478. <div>
  479. <div>
  480. <div id="conference_start" style="display: block">
  481. <!-- 会议布局: &nbsp; -->
  482. <select id="conf_layout" name="conf_layout" style="display: none">
  483. <option value="2x2">2x2</option>
  484. <option value="3x3">3x3</option>
  485. <option value="1up_top_left+3">一主三从</option>
  486. </select>
  487. &nbsp;
  488. <!-- 画布尺寸: -->
  489. <select id="conf_template" name="conf_template" style="display: none">
  490. <option value="480p" selected="selected">480x640</option>
  491. <option value="720p">720x1080</option>
  492. <option value="default">default</option>
  493. </select>
  494. &nbsp;
  495. 会议类型:
  496. <select id="conf_call_type2" name="conf_call_type2" >
  497. <!-- <option value="video">视频</option> -->
  498. <option value="audio">音频</option>
  499. </select>
  500. <input type="hidden" value="audio" id="conf_call_type" name="conf_call_type" />
  501. &nbsp;
  502. <input type="button" name="startConference" id="startConference"
  503. onclick="_phoneBar.conferenceStartBtnUI('')"
  504. style="width: 70px;" value="启动会议">
  505. &nbsp;
  506. <input type="button" name="endConference" id="endConference"
  507. onclick="_phoneBar.conferenceEnd()"
  508. disabled="disabled"
  509. style="width: 70px;" value="结束会议">
  510. </div>
  511. <div style="width: 100%;"> &nbsp; </div>
  512. <div id="conference_member_list" style="display: none">
  513. <ul>
  514. <li id="conference_header">
  515. <span class="conf_name"> <input id="member_name" name="member_name" placeholder="姓名" style="width: 60px;" /> </span> &nbsp;
  516. <span class="conf_phone"> <input id="member_phone" name="member_phone" placeholder="手机号" style="width: 110px;" /> </span> &nbsp;
  517. <span class="conf_call_type">
  518. <select id="member_call_type" name="member_call_type" style="display: none">
  519. <option value="video">视频</option>
  520. <option value="audio" selected>音频</option>
  521. </select>
  522. </span>
  523. <span class="conf_video_level" style="display: none">
  524. <select id="member_video_level" name="member_video_level">
  525. </select>
  526. </span>
  527. <span class="conf_name">
  528. <input type="button" name="addConfMember" id="addConfMember"
  529. onclick="_phoneBar.conferenceAddMemberBtnUI(0)"
  530. style="width: 70px;" value="加入会议">
  531. </span>
  532. </li>
  533. <!-- 会议成员展示模版html -->
  534. <li id="conf_member_template" style="display: none;">
  535. <span class="conf_name">{member_name}</span>
  536. <span class="conf_phone">{member_phone}</span>
  537. <span class="conf_mute"><a href="javascript:void(0)" onclick="_phoneBar.conferenceMuteMember('{member_phone}')"><img alt="禁言该成员。" src="images/mute.jpg" width="15" height="17" /> </a> </span>
  538. <span class="conf_vmute" style="display: none"><a href="javascript:void(0)" onclick="_phoneBar.conferenceVMuteMember('{member_phone}')"><img alt="关闭该成员的视频。" src="images/video.jpg" /> </a></span>
  539. <span class="conf_remove"><a href="javascript:void(0)" onclick="_phoneBar.conferenceRemoveMembers('{member_phone}')" title="踢除会议成员。">移除</a></span>
  540. <span class="conf_re_invite"><a href="javascript:void(0)" onclick="_phoneBar.conferenceAddMemberBtnUI(1, '{member_phone}', '{member_name}')" title="重新呼叫。">重呼</a></span>
  541. <span class="conf_status">{member_status}</span>
  542. </li>
  543. <li></li>
  544. </ul>
  545. </div>
  546. </div>
  547. </div>
  548. </td>
  549. </tr>
  550. <tr id="transfer_area" width="100%" style="display: none">
  551. <td colspan="2" width="100%" style="padding-left: 140px; padding-top: 30px;">
  552. <table width="100%">
  553. <tr>
  554. <td width="90">业务组 </td>
  555. <td width="90">坐席成员</td>
  556. <td>&nbsp; </td>
  557. </tr>
  558. <tr>
  559. <td>
  560. <select size="10" id="transfer_to_groupIds" name="transfer_to_groupIds">
  561. <option value="">请选择</option>
  562. </select>
  563. </td>
  564. <td>
  565. <select size="10" id="transfer_to_member" name="transfer_to_member">
  566. <option value="">请选择</option>
  567. </select>
  568. </td>
  569. <td valign="middle">
  570. &nbsp;&nbsp; <input type="text" name="externalPhoneNumber" id="externalPhoneNumber" placeholder="电话号码"
  571. title="可以把当前通话转接到外线号码上。 如果该文本框留空,则忽略处理。"
  572. class="tel_txt" />
  573. <br /> <br />
  574. &nbsp;&nbsp; <input type="button" name="doTransferBtn" id="doTransferBtn"
  575. onclick="_phoneBar.transferBtnClickUI()"
  576. style="width: 70px;" value="转接电话" title="把当前电话转接给他/她处理。" /> &nbsp;
  577. &nbsp;&nbsp; <input type="button" name="stopCallWait" id="stopCallWait"
  578. onclick="_phoneBar.stopCallWaitBtnClickUI()"
  579. style="width: 70px;" value="接回客户" title="在咨询失败的情况下使用该按钮,接回处于等待中的电话。" /> &nbsp;
  580. &nbsp;&nbsp; <input type="button" name="transferCallWait" id="transferCallWait"
  581. onclick="_phoneBar.transferCallWaitBtnClickUI()"
  582. style="width: 70px;" value="转接客户" title="在咨询成功的情况下使用该按钮,把电话转接给专家坐席。" /> &nbsp;
  583. <input type="button" name="doConsultationBtn" id="doConsultationBtn"
  584. onclick="_phoneBar.consultationBtnClickUI()"
  585. style="width: 70px;" value="拨号咨询" title="" />
  586. </td>
  587. </tr>
  588. </table>
  589. </td>
  590. </tr>
  591. </table>
  592. </form>
  593. <div id="chat-container">
  594. <div id="chat-messages" class="message-container"></div>
  595. </div>
  596. <script>
  597. // 以下是通话转接操作界面的功能
  598. const transferToGroupId = document.getElementById("transfer_to_groupIds");
  599. const transferToMember = document.getElementById("transfer_to_member");
  600. // 填充 transfer_to_groupId 数据
  601. function populateGroupIdOptions() {
  602. transferToGroupId.length = 0; //清除所有选项
  603. let groups = _phoneBar.callConfig.groups;
  604. groups.forEach(group => {
  605. const option = document.createElement("option");
  606. option.value = group.groupId;
  607. option.textContent = group.bizGroupName;
  608. transferToGroupId.appendChild(option);
  609. });
  610. if(transferToGroupId.selectedIndex == -1){
  611. transferToGroupId.selectedIndex = 0;
  612. }
  613. };
  614. // 根据选中的 groupId 填充 transfer_to_member 数据
  615. function populateMemberIdOptions(members, selectedGroupId) {
  616. if (!Array.isArray(members)) {
  617. console.error("populateMemberOptions: members is not a Array.", members);
  618. return;
  619. }
  620. transferToMember.innerHTML = '<option value="">请选择</option>';
  621. members
  622. .filter(member => member.groupId === selectedGroupId)
  623. .forEach(member => {
  624. const option = document.createElement("option");
  625. const statusMap = { 1 : "刚签入", 2: "空闲", 3: "忙碌", 4: "通话中", 5: "事后处理" };
  626. option.value = member.opnum;
  627. option.textContent = `${member.opnum}(${statusMap[member.agentStatus] || ""})`;
  628. transferToMember.appendChild(option);
  629. });
  630. };
  631. function refreshMemberIdList() {
  632. const selectedGroupId = transferToGroupId.value;
  633. if(selectedGroupId != "") {
  634. let origValue = transferToMember.value;
  635. populateMemberIdOptions(_phoneBar.callConfig.agentList, selectedGroupId);
  636. //判断原始选择项是否还存在,存在则重新赋值;
  637. let hasValue = transferToMember.querySelector(`option[value="${origValue}"]`) !== null;
  638. if(hasValue) {
  639. transferToMember.value = origValue;
  640. }
  641. }
  642. }
  643. /* asr实时对话文本框的功能 */
  644. _phoneBar.on(ccPhoneBarSocket.eventList.asr_process_started, function (msg) {
  645. $(chatMessages).html("");
  646. });
  647. _phoneBar.on(ccPhoneBarSocket.eventList.asr_result_generate, function (msg) {
  648. handleAsrMessage(msg);
  649. });
  650. _phoneBar.on(ccPhoneBarSocket.eventList.asr_process_end_customer, function (msg) {
  651. handleAsrMessage(msg);
  652. });
  653. _phoneBar.on(ccPhoneBarSocket.eventList.asr_process_end_agent, function (msg) {
  654. handleAsrMessage(msg);
  655. });
  656. const chatMessages = document.getElementById('chat-messages');
  657. $("#chat-container").hide();
  658. function handleAsrMessage(data) {
  659. $("#chat-container").show();
  660. const { status, object } = data;
  661. if (status === 619 && object) {
  662. const { role, text, vadType } = object;
  663. if(vadType == 1) {
  664. addMessageToChat(role, text);
  665. }
  666. } else if (status === 620 || status === 621) {
  667. addSystemMessage("对话已结束。");
  668. }
  669. }
  670. function addMessageToChat(role, text) {
  671. const messageDiv = document.createElement('div');
  672. messageDiv.className = 'message ' + (role === 1 ? 'customer' : 'agent');
  673. // 添加角色名称
  674. const roleHeader = document.createElement('div');
  675. roleHeader.className = 'message-header';
  676. roleHeader.textContent = role === 1 ? '客户' : '我';
  677. const messageContent = document.createElement('div');
  678. messageContent.textContent = text;
  679. // messageDiv.appendChild(roleHeader);
  680. messageDiv.appendChild(messageContent);
  681. chatMessages.appendChild(messageDiv);
  682. scrollToBottom();
  683. }
  684. function addSystemMessage(text) {
  685. const systemMessage = document.createElement('div');
  686. systemMessage.className = 'system-message';
  687. systemMessage.textContent = text;
  688. chatMessages.appendChild(systemMessage);
  689. scrollToBottom();
  690. }
  691. function scrollToBottom() {
  692. chatMessages.scrollTop = chatMessages.scrollHeight;
  693. }
  694. </script>
  695. </body>
  696. </html>