phone-bar-ex.html 34 KB

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