/* * 呼叫中心电话工具条 * author: easycallcenter365@126.com * 2025-04-08 * 修改:增强断开清理,优化重连状态,修复isCalling死锁 */ import $ from './jquery-1.11.0.js'; function _phoneBarObserver() { if (!(this instanceof _phoneBarObserver)) { return new _phoneBarObserver(); } this.listeners = {}; } _phoneBarObserver.prototype = { on: function(key, callback) { this.addListener(key, callback); }, off: function(key, callback) { this.removeListener(key, callback); }, addListener: function(key, callback) { if (!this.listeners[key]) this.listeners[key] = []; this.listeners[key].push(callback); }, removeListener: function(key, callback) { if (this.listeners[key]) { if (callback) { const index = this.listeners[key].indexOf(callback); if (index > -1) this.listeners[key].splice(index, 1); } else { this.listeners[key] = []; } } }, notifyAll: function(key, info) { var keys = [key]; if (key != null && String(key) !== key) keys.push(String(key)); keys.forEach(function(keyName) { if (keyName == null || !this.listeners[keyName]) return; for (var i = 0; i < this.listeners[keyName].length; i++) { if (typeof this.listeners[keyName][i] === 'function') { this.listeners[keyName][i](info); } } }, this); }, make: function(o) { for (var i in this) { o[i] = this[i]; o.listeners = {}; } } }; "use strict"; function ccPhoneBarSocket() { var observer = new _phoneBarObserver(); observer.make(this); var _cc = this; var ws = null; var wsuri = null; var _connectPending = false; var _sendQueue = []; var _kickedByOtherDevice = false; var _everConnected = false; var _reconnectSeq = 0; var _reconnectAttemptAt = 0; var _reconnect201RetryCount = 0; var _staleCloseTimer = null; var MAX_201_RETRY = 5; var CONNECT_RACE_WINDOW_MS = 12000; var STALE_CLOSE_DELAY_MS = 400; var RECOVERING_CALL_CLEAR_MS = 5000; var RETRY_201_BASE_MS = 400; var RETRY_201_STEP_MS = 300; var isConnected = false; var callConnected = false; var canSendVideoReInvite = false; this.subscribeAgentListStarted = false; this.iframe = null; this.isCalling = false; // 防止重复外呼 this._intentionalDisconnect = false; this.resetOutboundLock = function() { this.isCalling = false; }; this._activeCallUuid = null; this._recoveringCall = false; this._suppressSessionNotFound = false; this.setActiveCallUuid = function(uuid) { if (uuid) this._activeCallUuid = String(uuid); }; this.getActiveCallUuid = function() { return this._activeCallUuid; }; this.clearActiveCallUuid = function() { this._activeCallUuid = null; this._recoveringCall = false; }; this._captureCallUuid = function(msg) { if (msg && msg.object && msg.object.uuid) { this.setActiveCallUuid(msg.object.uuid); } }; this.restoreCallSession = function() { if (!this._activeCallUuid) return; if (!ws || ws.readyState !== WebSocket.OPEN) return; console.log('[IPCC] 重连后恢复本地通话状态 uuid=', this._activeCallUuid); this._recoveringCall = true; callConnected = true; }; this.callConfig = { 'useDefaultUi': false, 'ipccServer': 'sip.ylrzcloud.com', 'enableWss': true, 'callCodec': 'pcma', 'enableHeartBeat': true, 'heartBeatIntervalSecs': 16, 'useSameAudioCodeForOutbound': true, 'loginToken': '', 'gatewayList': [{ uuid: '01', updateTime: 1675953810492, gatewayAddr: '113.207.68.30:5543;external', callerNumber: '02386990208', calleePrefix: '', priority: 1, concurrency: 1000, register: false, audioCodec: 'g711' }], 'gatewayEncrypted': false, 'fsServer': '129.28.164.235:5060', 'groupId': '1', 'groups': null, 'agentList': null, 'phoneType': 'EyeBeam', 'webPhoneUrl': 'None', 'localHostProxyVersion': 'v20221130_1736', 'localHostProxyPort': 8888 }; this.insertAfter = function(newElement, targetElement) { var parent = targetElement.parentNode; if (parent.lastChild === targetElement) parent.appendChild(newElement); else parent.insertBefore(newElement, targetElement.nextSibling); }; this.trim = function(str) { return str.replace(/^\s\s*/, '').replace(/\s\s*$/, ''); }; this.getIsConnected = function() { return isConnected; }; this.getCallConnected = function() { return callConnected; }; this.setCallConnected = function(value) { callConnected = value; }; this.setCanSendVideoReInvite = function(value) { canSendVideoReInvite = value; }; this.getCanSendVideoReInvite = function() { return canSendVideoReInvite; }; this.heartbeatTimer = null; // 与 phone-bar-ex.html 一致:status 200 / 重连后重置心跳定时器 this.setHeartbeat = function() { if (this.heartbeatTimer) { clearInterval(this.heartbeatTimer); this.heartbeatTimer = null; } if (!_cc.callConfig.enableHeartBeat) return; var intervalSec = _cc.callConfig.heartBeatIntervalSecs || 16; this.heartbeatTimer = setInterval(function() { if (!_cc.callConfig.enableHeartBeat) return; if (!_cc.getIsConnected()) return; if (!ws || ws.readyState !== WebSocket.OPEN) { console.warn('[heartbeat] WebSocket未连接,停止心跳'); if (_cc.heartbeatTimer) { clearInterval(_cc.heartbeatTimer); _cc.heartbeatTimer = null; } return; } console.debug('[heartbeat] 发送心跳'); try { _cc.sendMsg({ action: 'setHearBeat', body: '{}' }); } catch (e) { console.error('[heartbeat] 发送心跳失败:', e); if (_cc.heartbeatTimer) { clearInterval(_cc.heartbeatTimer); _cc.heartbeatTimer = null; } } }, intervalSec * 1000); console.log('[heartbeat] 心跳已启动,间隔:', intervalSec, '秒'); }; this.loadScript = function(destUrl, callbackFunc) { var script = document.createElement("script"); script.type = "text/javascript"; script.src = destUrl; if (null != callbackFunc) script.onload = function() { callbackFunc(); }; document.getElementsByTagName('head')[0].appendChild(script); }; this.initConfig = function(config) { for (var element in config) this.callConfig[element] = config[element]; var _loginToken = this.callConfig["loginToken"]; if (typeof(_loginToken) == "undefined" || _loginToken === "") { alert("电话工具条:无法获取 loginToken!"); return; } console.log("callConfig:", JSON.stringify(this.callConfig)); if (_cc.callConfig.enableWss) { wsuri = 'wss://' + this.callConfig.ipccServer + '/call-center/websocketServer?' + '&loginToken=' + this.callConfig.loginToken; } else { wsuri = 'ws://129.28.164.235:1081/call-center/websocketServer?' + '&loginToken=' + this.callConfig.loginToken; } console.log("ipccServer url: " + wsuri); var ipccServerIpAddr = this.callConfig.ipccServer.split(":"); if (this.callConfig.enableWss && this.checkIP(ipccServerIpAddr)) { var tipsError = "ERROR! 启用了wss之后,必须使用域名访问websocketServer! " + this.callConfig.ipccServer; console.log(tipsError); alert(tipsError); } if (this.callConfig.enableHeartBeat) this.setHeartbeat(); if (_cc.callConfig["useDefaultUi"]) this.initPhoneBarUI(); }; this.checkIP = function(ip) { 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])$/; return re.test(ip); }; this.disconnect = function() { if (callConnected) { console.warn('[disconnect] 通话进行中,拒绝断开 IPPC 连接'); return; } _cc._performDisconnect(); }; /** 强制断开(清理/切线路时使用,忽略通话态) */ this.forceDisconnect = function() { callConnected = false; _cc._activeCallUuid = null; _cc._recoveringCall = false; _cc._performDisconnect(); }; this._performDisconnect = function() { console.log('[disconnect] 开始断开IPCC连接'); this._intentionalDisconnect = true; if (this.heartbeatTimer) { clearInterval(this.heartbeatTimer); this.heartbeatTimer = null; } if (!ws || ws.readyState !== WebSocket.OPEN) { if (ws) { ws.onmessage = null; ws.onclose = null; ws.onopen = null; ws.onerror = null; ws.close(); ws = null; } isConnected = false; _connectPending = false; this.isCalling = false; return; } try { ws.onmessage = null; ws.onclose = null; ws.onopen = null; ws.onerror = null; var cmdInfo = {}; cmdInfo.action = "setAgentStatus"; cmdInfo.body = { "cmd": "disconnect", "args": { "msg": "disconnection opt triggered by js client." } }; _cc.sendMsg(cmdInfo); console.log('[disconnect] 已发送断开指令'); } catch(e) { console.error('[disconnect] 发送断开指令失败:', e); } try { ws.close(); } catch(e) { console.error('[disconnect] 关闭WebSocket失败:', e); } ws = null; isConnected = false; _connectPending = false; this.isCalling = false; console.log('[disconnect] IPCC连接已完全断开'); }; /** 断开并等待 WebSocket 关闭,避免同 token 新连接与旧会话冲突导致 201 */ this.disconnectAndWait = function(timeoutMs) { timeoutMs = timeoutMs || 2000; var _ccRef = this; return new Promise(function(resolve) { if (callConnected) { console.warn('[disconnectAndWait] 通话进行中,跳过断开'); resolve(false); return; } var finished = false; function done() { if (finished) return; finished = true; _ccRef._intentionalDisconnect = true; _connectPending = false; isConnected = false; _ccRef.isCalling = false; resolve(true); } var timer = setTimeout(done, timeoutMs); if (_ccRef.heartbeatTimer) { clearInterval(_ccRef.heartbeatTimer); _ccRef.heartbeatTimer = null; } if (!ws) { clearTimeout(timer); done(); return; } var closing = ws; var state = closing.readyState; if (state === WebSocket.CLOSED) { ws = null; clearTimeout(timer); done(); return; } try { closing.onmessage = null; closing.onopen = null; closing.onerror = null; closing.onclose = function() { if (ws === closing) ws = null; clearTimeout(timer); done(); }; if (state === WebSocket.OPEN) { try { closing.send(JSON.stringify({ action: "setAgentStatus", body: { cmd: "disconnect", args: { msg: "disconnection opt triggered by js client." } } })); } catch (e) {} } closing.close(); } catch (e) { if (ws === closing) ws = null; clearTimeout(timer); done(); } }); }; this.getExtNum = function() { return this.callConfig.extnum; }; this.getOpNum = function() { return this.callConfig.opnum; }; this.getGroupId = function() { return this.callConfig.groupId; }; this.getGroups = function() { return JSON.parse(JSON.stringify(this.callConfig.groups)); }; this.findAgentByOpNum = function(opnum) { if (_cc.callConfig.agentList == null) return null; for (var key in _cc.callConfig.agentList) { let item = _cc.callConfig.agentList[key]; if (item["opnum"] === opnum) { item["_arrayIndex"] = key; return item; } } return null; }; this.updateAgentList = function(agentList) { for (var key in agentList) { let item = agentList[key]; var existItem = this.findAgentByOpNum(item["opnum"]); if (existItem != null) { var newStatus = item["agentStatus"]; var oldStatus = existItem["agentStatus"]; var logoutTime = item["logoutTime"]; if (logoutTime > 0) { console.info("delete offline user", item["opnum"], "index=", key); _cc.callConfig.agentList.splice(existItem["_arrayIndex"], 1); continue; } if (newStatus != oldStatus) existItem["agentStatus"] = newStatus; } else { if (_cc.callConfig.agentList != null) _cc.callConfig.agentList.push(item); } } }; function _isCurrentSocket(socket) { return ws === socket; } function _safeSendOnSocket(socket, jsonObject) { if (_kickedByOtherDevice) { console.warn('[sendMsg] 账号已在其他设备登录,拒绝发送'); return false; } if (!socket) return false; var state = socket.readyState; if (state === WebSocket.OPEN) { try { socket.send(JSON.stringify(jsonObject)); return true; } catch (e) { console.error('[sendMsg] 发送失败:', e); return false; } } if (state === WebSocket.CONNECTING && socket === ws) { _sendQueue.push(jsonObject); return true; } console.warn('[sendMsg] WebSocket 未就绪, readyState=', state); return false; } function _flushSendQueue(socket) { if (!_isCurrentSocket(socket) || socket.readyState !== WebSocket.OPEN) return; while (_sendQueue.length) { try { socket.send(JSON.stringify(_sendQueue.shift())); } catch (e) { console.error('[sendMsg] 刷新发送队列失败:', e); break; } } } this.resetKickedState = function() { _kickedByOtherDevice = false; _reconnect201RetryCount = 0; }; this._handleKickedByOtherDevice = function(msg, sourceSocket) { if (sourceSocket && ws && ws !== sourceSocket) { if (isConnected && ws.readyState === WebSocket.OPEN) { console.warn('[IPCC] 已有有效连接,忽略过期连接的201'); try { sourceSocket.onmessage = null; sourceSocket.onclose = null; sourceSocket.close(); } catch (e) {} return; } } if (isConnected && ws && ws.readyState === WebSocket.OPEN && (!sourceSocket || sourceSocket === ws)) { console.error('[IPCC] 当前连接收到201,确认被踢下线'); } console.error('[IPCC] 用户已在其他设备登录(201),停止重连'); _kickedByOtherDevice = true; _connectPending = false; _cc._intentionalDisconnect = true; isConnected = false; _sendQueue = []; if (_cc.heartbeatTimer) { clearInterval(_cc.heartbeatTimer); _cc.heartbeatTimer = null; } _cc.notifyAll(ccPhoneBarSocket.eventList.user_login_on_other_device, msg); var closing = sourceSocket || ws; if (closing) { try { closing.onmessage = null; closing.onclose = null; closing.onopen = null; closing.onerror = null; closing.close(); } catch (e) {} if (ws === closing) ws = null; } }; function _scheduleOpenAfterStale(mySeq, delayMs) { if (_staleCloseTimer) clearTimeout(_staleCloseTimer); _staleCloseTimer = setTimeout(function() { _staleCloseTimer = null; if (mySeq !== _reconnectSeq || _kickedByOtherDevice) { _connectPending = false; return; } _createWebSocketConnection(); }, delayMs); } this.connect = function() { console.log('[connect] 开始建立IPCC WebSocket连接'); if (_kickedByOtherDevice) { console.warn('[connect] 已在其他设备登录,停止重连'); return; } if (ws && ws.readyState === WebSocket.OPEN) { if (callConnected) { console.log('[connect] WebSocket 已连接且通话中,跳过重连'); return; } console.log('[connect] WebSocket 已连接,无需重复连接'); _connectPending = false; return; } if (ws && ws.readyState === WebSocket.CONNECTING) { console.log('[connect] WebSocket 正在连接中,请勿重复操作'); return; } if (_connectPending) { console.log('[connect] 连接任务已排队,跳过重复 connect'); return; } _connectPending = true; _reconnectAttemptAt = Date.now(); _reconnectSeq++; var mySeq = _reconnectSeq; this._intentionalDisconnect = false; if (ws) { console.log('[connect] 检测到旧连接,等待关闭后再建连, readyState:', ws.readyState); var stale = ws; isConnected = false; _sendQueue = []; try { stale.onmessage = null; stale.onopen = null; stale.onerror = null; stale.onclose = function(evt) { if (ws === stale) ws = null; console.log('[connect] 旧连接已关闭, code=', evt && evt.code); if (mySeq !== _reconnectSeq) { _connectPending = false; return; } _scheduleOpenAfterStale(mySeq, STALE_CLOSE_DELAY_MS); }; if (stale.readyState === WebSocket.OPEN || stale.readyState === WebSocket.CONNECTING) { stale.close(); } else if (ws === stale) { ws = null; _scheduleOpenAfterStale(mySeq, 400); } } catch (e) { console.error('[connect] 关闭旧连接失败:', e); if (ws === stale) ws = null; _scheduleOpenAfterStale(mySeq, STALE_CLOSE_DELAY_MS); } return; } _scheduleOpenAfterStale(mySeq, 100); }; this.reconnect = function() { this.connect(); }; function _createWebSocketConnection() { console.log('[connect] 开始创建新的WebSocket连接'); if (!('WebSocket' in window)) { _connectPending = false; console.log('您的浏览器不支持 websocket,您无法使用本页面的功能!'); return; } var socket = new WebSocket(wsuri); ws = socket; _sendQueue = []; socket.onmessage = function(evt) { if (!_isCurrentSocket(socket)) return; console.log("recv msg from websocket server: ", evt.data); var msg = JSON.parse(evt.data); console.log("parsed json data:", msg); var resp_status = msg["status"]; switch (resp_status) { case 200: _everConnected = true; _connectPending = false; _reconnect201RetryCount = 0; isConnected = true; _cc.callConfig.extnum = msg.object["extnum"]; _cc.callConfig.opnum = msg.object["opnum"]; _cc.callConfig.groupId = msg.object["groupId"]; _cc.callConfig.groups = msg.object["groups"]; console.log('ipcc 连接成功.', 'connected_ipcc_server'); _cc.notifyAll(ccPhoneBarSocket.eventList.ws_connected, msg); if (callConnected || _cc._activeCallUuid) { _cc.restoreCallSession(); setTimeout(function() { if (_cc.getIsConnected()) _cc._recoveringCall = false; }, RECOVERING_CALL_CLEAR_MS); } else { _safeSendOnSocket(socket, { action: "setAgentStatus", body: { cmd: "setStatus", args: { status: ccPhoneBarSocket.agentStatusEnum.busy } } }); } _cc.setHeartbeat(); break; default: if (parseInt(resp_status) === 201) { if (isConnected && ws && ws !== socket && ws.readyState === WebSocket.OPEN) { console.warn('[IPCC] 有效连接已建立,忽略迟到的201'); try { socket.close(); } catch (e) {} return; } var isConnectRace = (Date.now() - _reconnectAttemptAt < CONNECT_RACE_WINDOW_MS) && _reconnect201RetryCount < MAX_201_RETRY; if (isConnectRace && _isCurrentSocket(socket)) { _reconnect201RetryCount++; console.warn('[IPCC] 连接竞态201,第', _reconnect201RetryCount, '次自动重试'); _connectPending = false; isConnected = false; try { socket.onmessage = null; socket.onclose = null; socket.onopen = null; socket.onerror = null; socket.close(); } catch (e) {} if (ws === socket) ws = null; setTimeout(function() { if (!_kickedByOtherDevice && !(isConnected && ws && ws.readyState === WebSocket.OPEN)) { _cc.connect(); } }, RETRY_201_BASE_MS + _reconnect201RetryCount * RETRY_201_STEP_MS); return; } _cc._handleKickedByOtherDevice(msg, socket); return; } _cc._captureCallUuid(msg); if (parseInt(resp_status) === parseInt(ccPhoneBarSocket.eventList.asr_result_generate)) { _cc._recoveringCall = false; } var hangupCode = parseInt(resp_status); var isHangupEvent = hangupCode === parseInt(ccPhoneBarSocket.eventList.caller_hangup) || hangupCode === parseInt(ccPhoneBarSocket.eventList.callee_hangup) || hangupCode === parseInt(ccPhoneBarSocket.eventList.CONFERENCE_MODERATOR_HANGUP); if (isHangupEvent) { _cc.notifyAll(resp_status, msg); } if (parseInt(resp_status) === 400) { var errText = (msg["msg"] || "").toString(); if (errText.indexOf("method not support") >= 0) { console.debug('[SDK] 服务端不支持的方法,已忽略:', errText); break; } if (errText.indexOf("通话不存在") >= 0 || errText.indexOf("不存在") >= 0) { if (_cc._suppressSessionNotFound || !_cc.getCallConnected()) { console.debug('[SDK] 挂机后会话已不存在,已忽略:', errText); } else { console.warn('[SDK] 服务端无通话会话:', errText); _cc.notifyAll('call_session_not_found', msg); } } else { _cc.isCalling = false; console.log('[SDK] 请求参数错误, status: 400', errText); } } else if (parseInt(resp_status) === parseInt(ccPhoneBarSocket.eventList.server_error)) { _cc.isCalling = false; console.log('[SDK] 发生错误,已重置 isCalling = false, status:', resp_status); } if (parseInt(resp_status) === parseInt(ccPhoneBarSocket.eventList.caller_hangup) || parseInt(resp_status) === parseInt(ccPhoneBarSocket.eventList.CONFERENCE_MODERATOR_HANGUP)) { _cc.setCallConnected(false); _cc.setCanSendVideoReInvite(false); _cc.callConfig.agentList = null; _cc.unSubscribeAgentList(); _cc.isCalling = false; _cc.clearActiveCallUuid(); console.log("caller_hangup"); } if (parseInt(resp_status) === parseInt(ccPhoneBarSocket.eventList.caller_answered)) { // 外呼:600=分机已接通,客户仍在振铃;桥接期需标记通话活跃,避免误判挂机 _cc.setCallConnected(true); } if (parseInt(resp_status) === parseInt(ccPhoneBarSocket.eventList.callee_hangup)) { _cc.isCalling = false; _cc.setCallConnected(false); _cc.clearActiveCallUuid(); } if (parseInt(resp_status) === parseInt(ccPhoneBarSocket.eventList.extension_off_line)) { _cc.isCalling = false; if (_cc.getCallConnected()) { _cc.setCallConnected(false); _cc.clearActiveCallUuid(); } } if (parseInt(resp_status) === parseInt(ccPhoneBarSocket.eventList.callee_answered)) { _cc.setCallConnected(true); _cc._recoveringCall = false; console.log("callee_answered"); } if (parseInt(resp_status) === parseInt(ccPhoneBarSocket.eventList.OUTBOUND_START)) { _cc.setCallConnected(true); } if (parseInt(resp_status) === parseInt(ccPhoneBarSocket.eventList.callee_busy)) { console.warn("callee_busy or rejected"); _cc.isCalling = false; } if (parseInt(resp_status) === parseInt(ccPhoneBarSocket.eventList.new_inbound_call)) { _cc.setCallConnected(true); console.log("new_inbound_call"); } if (parseInt(resp_status) === parseInt(ccPhoneBarSocket.eventList.caller_answered) || parseInt(resp_status) === parseInt(ccPhoneBarSocket.eventList.callee_answered)) { if (msg["object"]["callType"] === "audio") { _cc.setCanSendVideoReInvite(true); _cc.notifyAll(ccPhoneBarSocket.eventList.on_audio_call_connected, "audio call connected."); console.log("current callType is audio."); } if (msg["object"]["callType"] === "video") { _cc.setCanSendVideoReInvite(false); _cc.notifyAll(ccPhoneBarSocket.eventList.on_video_call_connected, "video call connected."); console.log("current callType is video."); } } if (parseInt(resp_status) === parseInt(ccPhoneBarSocket.eventList.customer_channel_hold)) _cc.changeUiOnHold(); if (parseInt(resp_status) === parseInt(ccPhoneBarSocket.eventList.customer_channel_unhold)) _cc.changeUiOnUnHold(); if (parseInt(resp_status) === parseInt(ccPhoneBarSocket.eventList.customer_on_hold_hangup)) { _cc.changeUiOnUnHold(); $("#holdBtn").removeClass('on'); $("#callStatus").text("保持的通话已挂机."); } if (parseInt(resp_status) === parseInt(ccPhoneBarSocket.eventList.customer_channel_call_wait)) { $("#stopCallWait").show(); $("#doConsultationBtn").hide(); $("#callStatus").text("客户电话等待中."); _cc.showTransferAreaUI(); } if (parseInt(resp_status) === parseInt(ccPhoneBarSocket.eventList.customer_channel_off_call_wait)) { $("#stopCallWait").hide(); $("#transferCallWait").hide(); _cc.hideTransferAreaUI(); $("#callStatus").text("等待的电话已接回."); } if (parseInt(resp_status) === parseInt(ccPhoneBarSocket.eventList.inner_consultation_start)) { $("#callStatus").text("咨询已开始."); $("#transferCallWait").show(); } if (parseInt(resp_status) === parseInt(ccPhoneBarSocket.eventList.inner_consultation_stop)) { $("#callStatus").text("咨询已结束."); $("#transferCallWait").hide(); } if (parseInt(resp_status) === parseInt(ccPhoneBarSocket.eventList.customer_on_call_wait_hangup)) { $("#stopCallWait").hide(); $("#transferCallWait").hide(); $("#callStatus").text("等待的客户已挂机."); } if (parseInt(resp_status) === parseInt(ccPhoneBarSocket.eventList.inner_consultation_start)) { $("#holdBtn").removeClass('on'); $("#hangUpBtn").removeClass('on'); $("#transferBtn").removeClass('on'); } if (parseInt(resp_status) === parseInt(ccPhoneBarSocket.eventList.transfer_call_success)) { var extNum = msg["object"]["callee"]; if (extNum === _cc.getExtNum()) { $("#holdBtn").addClass('on'); $("#hangUpBtn").addClass('on'); $("#transferBtn").addClass('on'); } } if (parseInt(resp_status) === parseInt(ccPhoneBarSocket.eventList.agent_status_data_changed)) { if (_cc.subscribeAgentListStarted) { if (_cc.callConfig.agentList == null) _cc.callConfig.agentList = JSON.parse(msg.object); else _cc.updateAgentList(JSON.parse(msg.object)); } } if (!isHangupEvent) { _cc.notifyAll(resp_status, msg); } break; } }; socket.onclose = function(evt) { if (!_isCurrentSocket(socket)) { console.log('[onclose] 忽略已作废连接的关闭事件'); return; } console.log('[onclose] IPCC WebSocket关闭事件触发'); console.log('[onclose] evt.code:', evt.code, ', evt.reason:', evt.reason, ', evt.wasClean:', evt.wasClean); const intentional = _cc._intentionalDisconnect || _kickedByOtherDevice; _cc._intentionalDisconnect = false; _connectPending = false; isConnected = false; _sendQueue = []; if (_cc.heartbeatTimer) { clearInterval(_cc.heartbeatTimer); _cc.heartbeatTimer = null; } ws = null; if (_kickedByOtherDevice) { console.log('[onclose] 其他设备登录导致断开'); return; } if (!callConnected && !_cc._activeCallUuid) { _cc.isCalling = false; } if (intentional || (evt.wasClean && evt.code === 1000)) { console.log('[onclose] 主动/正常关闭,不触发异常断开事件'); } else { console.warn('[onclose] 异常断开,通知前端'); _cc.notifyAll(ccPhoneBarSocket.eventList.ws_disconnected, "ipccserver 连接断开."); } console.log('[onclose] 连接状态已更新'); }; socket.onerror = function(evt) { if (!_isCurrentSocket(socket)) return; console.warn('[onerror] IPCC WebSocket 错误', evt); }; socket.onopen = function(evt) { if (!_isCurrentSocket(socket)) return; console.log('ipccserver websocket onopen...'); _flushSendQueue(socket); }; } this.sendMsg = function(jsonObject) { console.debug("ws.send:", jsonObject); return _safeSendOnSocket(ws, jsonObject); }; this.changeUiOnHold = function() { $("#holdBtnLi").hide(); $("#unHoldBtnLi").show(); $("#unHoldBtn").addClass('on'); }; this.changeUiOnUnHold = function() { $("#holdBtnLi").show(); $("#holdBtn").addClass('on'); $("#unHoldBtnLi").hide(); }; this.hideTransferAreaUI = function() { var transferArea = document.getElementById("transfer_area"); if (transferArea) transferArea.style.display = "none"; }; this.showTransferAreaUI = function() { var transferArea = document.getElementById("transfer_area"); if (transferArea) transferArea.style.display = "block"; }; this.setStatusFree = function() { this.setStatus(ccPhoneBarSocket.agentStatusEnum.free); }; this.setStatusBusy = function() { this.setStatus(ccPhoneBarSocket.agentStatusEnum.busy); }; ccPhoneBarSocket.utils = { getQueryParam: function(paramName) { const url = window.location.href; const params = new URLSearchParams(new URL(url).search); return params.get(paramName); } }; ccPhoneBarSocket.agentStatusEnum = { "justLogin": 1, "free": 2, "busy": 3, "busy_rest": 31, "busy_meeting": 32, "busy_training": 33, "incall": 4, "fill_form": 5, "conference": 6 }; ccPhoneBarSocket.videoLevels = { "Smooth": { "levelId": "42e00b", "description": "流畅" }, "Smooth2": { "levelId": "42e00c", "description": "流畅+" }, "Smooth3": { "levelId": "42e00d", "description": "流畅++" }, "Clear": { "levelId": "42e014", "description": "清晰" }, "Clear2": { "levelId": "42e015", "description": "清晰+" }, "Clear3": { "levelId": "42e016", "description": "清晰++" }, "HD": { "levelId": "42e01e", "description": "高清" }, "HD2": { "levelId": "42e01f", "description": "高清+" } }; ccPhoneBarSocket.eventList = { "on_audio_call_connected": "100", "on_video_call_connected": "101", "ws_connected": "200", "user_login_on_other_device": "201", "ws_disconnected": "202", "call_session_status_data_changed": "203", "agent_status_data_changed": "205", "request_args_error": "400", "unauthorized": "401", "server_error": "500", "server_error_audio_codec_not_match": "501", "caller_answered": "600", "caller_hangup": "601", "caller_busy": "602", "caller_not_login": "603", "caller_respond_timeout": "604", "callee_answered": "605", "callee_hangup": "606", "callee_ringing": "607", "status_changed": "608", "callee_busy": "609", "outbound_finished": "611", "PREDICTIVE_CALL_INBOUND": "612", "new_inbound_call": "613", "acd_group_queue_number": "615", "transfer_call_recv": 616, "lock_agent_fail": 617, "transfer_call_success": 618, "asr_result_generate": 619, "asr_process_end_agent": 620, "asr_process_end_customer": 621, "asr_process_started": 622, "customer_channel_hold": 623, "customer_channel_unhold": 624, "customer_on_hold_hangup": 625, "inner_consultation_request": 626, "customer_channel_call_wait": 627, "customer_channel_off_call_wait": 628, "customer_on_call_wait_hangup": 629, "extension_on_line": 630, "extension_off_line": 631, "inner_consultation_start": 632, "inner_consultation_stop": 633, "conference_repeat_callee": 660, "CONFERENCE_CALL_MODERATOR_TIMEOUT": 661, "CONFERENCE_MEMBER_ANSWERED": 662, "CONFERENCE_MEMBER_HANGUP": 663, "CONFERENCE_MEMBER_MUTED_SUCCESS": 666, "CONFERENCE_MEMBER_MUTED_FAILED": 665, "CONFERENCE_MEMBER_UNMUTED_SUCCESS": 667, "CONFERENCE_MEMBER_UNMUTED_FAILED": 668, "CONFERENCE_MEMBER_NOT_EXISTS": "669", "CONFERENCE_MODERATOR_RESET": "670", "CONFERENCE_MODERATOR_ANSWERED": "671", "CONFERENCE_MODERATOR_HANGUP": "672", "CONFERENCE_MEMBER_VMUTED_SUCCESS": "674", "CONFERENCE_MEMBER_UnVMUTED_SUCCESS": "676", "CONFERENCE_MEMBER_UnVMUTED_FAILED": "677", "CONFERENCE_TRANSFER_SUCCESS_FROM_EXISTED_CALL": "678", "OUTBOUND_START": "679" }; this.createIframe = function(src) { var _iframe = document.createElement("iframe"); _iframe.style.width = '0'; _iframe.style.height = '0'; _iframe.style.margin = '0'; _iframe.style.padding = '0'; _iframe.style.overflow = 'hidden'; _iframe.style.border = 'none'; _iframe.src = src; document.body.appendChild(_iframe); _cc.iframe = _iframe; }; this.openSoftPhone = function() { var softPhoneUrl = "http://127.0.0.1:" + _cc.callConfig["localHostProxyPort"] + "/autoSetExtension?server=" + encodeURIComponent(_cc.callConfig["fsServer"].split(':')[0]) + "&port=" + _cc.callConfig["fsServer"].split(':')[1] + "&extnum=" + _cc.callConfig["extnum"] + "&pass=" + encodeURIComponent(_cc.callConfig["extPassword"]) + "&phoneType=" + _cc.callConfig["phoneType"] + "&version=" + encodeURIComponent(_cc.callConfig["localHostProxyVersion"]) + "&webPhoneUrl=" + encodeURIComponent(_cc.callConfig["webPhoneUrl"]); console.log("softPhoneUrl:", softPhoneUrl); _cc.iframe.src = softPhoneUrl; }; this.setStatus = function(status) { this.sendMsg({ action: "setAgentStatus", body: { cmd: "setStatus", args: { status: status } } }); }; this.logOff = function() { this.sendMsg({ action: "setAgentStatus", body: { cmd: "disconnect", args: { cause: "disconnect request from js client." } } }); }; this.stopCallWaitBtnClickUI = function() { this.sendMsg({ action: "callWait", body: { cmd: "stop", args: {} } }); }; this.transferCallWaitBtnClickUI = function() { this.callControl("transferCallWait", {}); }; this.consultationBtnClickUI = function() { let transferType = "outer"; let phoneNumber = $("#externalPhoneNumber").val().trim(); if (phoneNumber === "") { transferType = "inner"; var groupId = $("#transfer_to_groupIds").val(); if ($.trim(groupId) == "") { alert("请选择业务组!"); $("#transfer_to_groupIds").focus(); return; } var member = $("#transfer_to_member").val(); if ($.trim(member) == "") { alert("请选择要咨询的坐席成员!"); $("#transfer_to_member").focus(); return; } var selectText = $('#transfer_to_member option:selected').text(); if (selectText.indexOf("空闲") == -1) { alert("请选择空闲的坐席成员!"); $("#transfer_to_member").focus(); return; } if (member == this.getOpNum()) { alert("不能咨询自己,请选择其他坐席成员!"); return; } phoneNumber = member; } this.callControl("consultation", { "to": phoneNumber, "transferType": transferType }); }; this.transferBtnClickUI = function() { let transferType = "outer"; let phoneNumber = $("#externalPhoneNumber").val().trim(); if (phoneNumber === "") { transferType = "inner"; var groupId = $("#transfer_to_groupIds").val(); if ($.trim(groupId) === "") { alert("请选择转接的业务组!"); $("#transfer_to_groupIds").focus(); return; } var member = $("#transfer_to_member").val(); if ($.trim(member) === "") { alert("请选择转接的坐席成员!"); $("#transfer_to_member").focus(); return; } var selectText = $('#transfer_to_member option:selected').text(); if (selectText.indexOf("空闲") === -1) { alert("请选择空闲的坐席成员!"); $("#transfer_to_member").focus(); return; } if (member === this.getOpNum()) { alert("不能转给自己,请选择其他坐席成员!"); return; } phoneNumber = member; } this.transferCall(phoneNumber, transferType); }; this.transferCall = function(userCodeOrPhone, transferType) { if (transferType === "inner") { if (userCodeOrPhone !== this.getOpNum()) this.callControl("transferCall", { "to": userCodeOrPhone, "transferType": "inner" }); else console.error("cant not transfer call to yourself."); } else { this.callControl("transferCall", { "to": userCodeOrPhone, "transferType": "outer" }); } }; this.hangup = function() { var args = {}; if (_cc._activeCallUuid) args.uuid = _cc._activeCallUuid; _cc._suppressSessionNotFound = true; setTimeout(function() { _cc._suppressSessionNotFound = false; }, 4000); var sent = this.callControl("endSession", args); this.isCalling = false; if (sent !== false) { setTimeout(function() { _cc.setCallConnected(false); _cc.clearActiveCallUuid(); }, 200); } else { _cc.setCallConnected(false); _cc.clearActiveCallUuid(); } }; this.callControl = function(action, argsObject) { if (!ws || ws.readyState !== WebSocket.OPEN) { console.warn('[callControl] WebSocket 未连接, cmd=', action); return false; } var sessionControl = {}; sessionControl.action = "call"; sessionControl.body = { "cmd": action, "args": argsObject || {} }; return this.sendMsg(sessionControl); }; this.checkCallConfirmed = function() { if (!_cc.getIsConnected()) { console.log('请先上线.'); return false; } if (!_cc.getCallConnected()) { console.log('当前没有通话.'); return false; } return true; }; this.sendVideoFile = function(mp4FilePath) { if (!_cc.checkCallConfirmed()) return false; if (typeof(mp4FilePath) == "undefined" || mp4FilePath.trim().length === 0) { console.log("Parameter mp4FilePath is missing!"); return false; } this.callControl("playMp4File", { "mp4FilePath": mp4FilePath }); return true; }; this.reInviteVideoCall = function() { if (!_cc.checkCallConfirmed()) return false; if (!_cc.getCanSendVideoReInvite()) { console.log('cant not send video reInvite.'); return false; } this.callControl("reInviteVideo", {}); return true; }; this.call = function(phoneNumber, callType, videoLevel) { if (this.isCalling) { console.warn('[SDK] 正在呼叫中,请勿重复操作 (isCalling=true)'); return false; } if (typeof(videoLevel) == "undefined" || videoLevel.trim().length === 0) { videoLevel = ccPhoneBarSocket.videoLevels.HD.levelId; console.log("auto default set videoLevel=", videoLevel); } if (typeof(callType) == "undefined" || callType.trim().length === 0) { callType = "audio"; console.log("auto default set callType=", callType); } if (phoneNumber == null || phoneNumber.length === 0) { console.log('请输入外呼号码!'); return false; } if (phoneNumber.trim().length < 3) { alert('请输入正确格式的外呼号码!'); return false; } if (!_cc.getIsConnected()) { console.log('请先上线.'); return false; } let outboundInfo = { "gatewayList": _cc.callConfig.gatewayList, "destPhone": phoneNumber, "gatewayEncrypted": _cc.callConfig.gatewayEncrypted, "useSameAudioCodeForOutbound": _cc.callConfig.useSameAudioCodeForOutbound, "callType": callType, "videoLevel": videoLevel }; this.callControl("startSession", outboundInfo); this.isCalling = true; return true; }; this.callEx = function(phoneNumber) { if (phoneNumber == null || phoneNumber.length === 0) { console.log('请输入外呼号码!'); return; } if (!_cc.getIsConnected()) { _cc.connect(); _cc.on(ccPhoneBarSocket.eventList.ws_connected, function() { _cc.off(ccPhoneBarSocket.eventList.ws_connected); _cc.call(phoneNumber); }); return; } _cc.call(phoneNumber); }; // 以下为UI工具条相关方法(仅当 useDefaultUi=true 时使用,不影响核心功能) ccPhoneBarSocket.findItemByCode = function(code) { for (var item in ccPhoneBarSocket.eventListWithTextInfo) { if (ccPhoneBarSocket.eventListWithTextInfo[item].code === code) return ccPhoneBarSocket.eventListWithTextInfo[item]; } }; ccPhoneBarSocket.eventListWithTextInfo = { "ws_connected": { "code": 200, msg: "已签入", btn_text: [{ id: "#onLineBtn", name: "签出" }], enabled_btn: ['#setFree', '#callBtn', '#onLineBtn', '#consultationBtn'] }, "ws_disconnected": { "code": 202, msg: "服务器连接断开", btn_text: [{ id: "#onLineBtn", name: "签入" }], enabled_btn: ['#onLineBtn'] }, "user_login_on_other_device": { "code": 201, msg: "用户已在其他设备登录", btn_text: [{ id: "#onLineBtn", name: "签入" }], enabled_btn: ['#onLineBtn'] }, "request_args_error": { "code": 400, msg: "客户端请求参数错误", btn_text: [], enabled_btn: [] }, "server_error": { "code": 500, msg: "服务器内部错误", btn_text: [], enabled_btn: [] }, "caller_answered": { "code": 600, msg: "分机已接通", btn_text: [], enabled_btn: ['#resetStatus', '#hangUpBtn', '#transferBtn', '#holdBtn', '#consultationBtn'] }, "caller_hangup": { "code": 601, msg: "分机已挂断", btn_text: [], enabled_btn: ['#onLineBtn', '#resetStatus', '#callBtn', '#setFree', '#consultationBtn'] }, "caller_busy": { "code": 602, msg: "分机忙,上一通电话未挂断", btn_text: [], enabled_btn: ['#onLineBtn', '#resetStatus', '#callBtn', '#setFree', '#consultationBtn'] }, "caller_not_login": { "code": 603, msg: "分机未登录,请检查", btn_text: [], enabled_btn: ['#onLineBtn', '#resetStatus', '#callBtn', '#setFree', '#consultationBtn'] }, "caller_respond_timeout": { "code": 604, msg: "分机未应答超时,请重新打开分机", btn_text: [], enabled_btn: ['#onLineBtn', '#resetStatus', '#callBtn', '#setFree', '#consultationBtn'] }, "callee_answered": { "code": 605, msg: "被叫已接通", btn_text: [], enabled_btn: ['#resetStatus', '#hangUpBtn', '#transferBtn', '#holdBtn', '#consultationBtn'] }, "callee_hangup": { "code": 606, msg: "通话结束", btn_text: [], enabled_btn: ['#onLineBtn', '#resetStatus', '#callBtn', '#setFree', '#consultationBtn'] }, "callee_ringing": { "code": 607, msg: "被叫振铃中", btn_text: [], enabled_btn: ['#resetStatus', '#hangUpBtn', '#transferBtn', '#consultationBtn'] }, "status_changed": { "code": 608, msg: "状态已改变", btn_text: [], enabled_btn: [] }, "free": { "code": 0, msg: "空闲中", btn_text: [], enabled_btn: ['#setBusy', '#onLineBtn', '#consultationBtn'] }, "busy": { "code": 1, msg: "忙碌", btn_text: [], enabled_btn: ['#setFree', '#onLineBtn', '#callBtn', '#consultationBtn'] }, "customer_channel_hold": { "code": 623, msg: "通话已保持.", btn_text: [], enabled_btn: ['#setFree', '#callBtn', '#unHoldBtn', '#consultationBtn'] }, "customer_channel_unhold": { "code": 624, msg: "通话已接回.", btn_text: [], enabled_btn: ['#hangUpBtn', '#holdBtn'] } }; ccPhoneBarSocket.phone_buttons = ['#setFree', '#setBusy', '#callBtn', '#hangUpBtn', '#resetStatus', '#onLineBtn', '#transferBtn', '#holdBtn', '#unHoldBtn', '#consultationBtn']; this.updatePhoneBar = function(msg, status_key) { if (!_cc.callConfig.useDefaultUi) { console.log("callConfig.useDefaultUi = false , 已禁用默认ui工具条按钮."); return; } if (msg) $("#callStatus").text(msg.msg); var status_info = ccPhoneBarSocket.findItemByCode(status_key); if (!status_info) return; if (status_info.code === ccPhoneBarSocket.eventListWithTextInfo.status_changed.code) { if (msg.object.status === ccPhoneBarSocket.agentStatusEnum.free) status_info = ccPhoneBarSocket.eventListWithTextInfo.free; else status_info = ccPhoneBarSocket.eventListWithTextInfo.busy; } var msgSet = status_info.msg; if (msgSet && msgSet.length > 0) $("#callStatus").text(msgSet); var btn_text = status_info.btn_text; var enabled_btn = status_info.enabled_btn; if (btn_text) $.each(btn_text, function(i, d) { $(d.id).next().text(d.name); }); if (enabled_btn.length === 0) return; var all_btn = ccPhoneBarSocket.phone_buttons; for (var i in all_btn) { if ($.inArray(all_btn[i], enabled_btn) < 0) $(all_btn[i]).removeClass('on'); else $(enabled_btn[$.inArray(all_btn[i], enabled_btn)]).addClass('on'); } }; this.initPhoneBarUI = function() { window.onbeforeunload = function() { if (!confirm('关闭网页将导致您无法接听电话,确定要关闭吗 ?')) return false; }; $("#unHoldBtnLi").hide(); if (!_cc.callConfig.useDefaultUi) { console.log("callConfig.useDefaultUi = false , 已禁用默认ui工具条按钮."); return; } $('#conferenceBtn').on('click', function() { if (!_cc.getIsConnected()) { console.log('请先上线.'); return; } var confObjId = document.getElementById("conference_area"); if (confObjId.style.display === "block") confObjId.style.display = "none"; else confObjId.style.display = "block"; }); $('#callBtn').on('click', function() { if ($(this).hasClass('on')) { var destPhone = $.trim($("#ccphoneNumber").val()); var videoLevel = document.getElementById("videoLevelSelect").value; var callType = document.forms[0].callType.value; _cc.call(destPhone, callType, videoLevel); } }); $('#setFree').on('click', function() { if ($(this).hasClass('on')) _cc.setStatus(ccPhoneBarSocket.agentStatusEnum.free); }); $('#setBusy').on('click', function() { if ($(this).hasClass('on')) _cc.setStatus(ccPhoneBarSocket.agentStatusEnum.busy); }); $('#setBusySubList').on('change', function() { let itemValue = $('#setBusySubList').val(); _cc.setStatus(itemValue); }); $('#hangUpBtn').on('click', function() { if ($(this).hasClass('on')) _cc.hangup(); }); $('#holdBtn').on('click', function() { if ($(this).hasClass('on')) _cc.holdCall(); }); $('#unHoldBtn').on('click', function() { if ($(this).hasClass('on')) _cc.unHoldCall(); }); $("#doTransferBtn").hide(); function populateGroupIdOptions() { var groups = _cc.callConfig.groups; if (groups) { var selectObj = document.getElementById("transfer_to_groupIds"); if (selectObj && selectObj.tagName.toLowerCase() === 'select') { var firstOption = selectObj.options[0]; selectObj.innerHTML = ''; selectObj.add(firstOption); for (var i = 0; i < groups.length; i++) { var group = groups[i]; var option = document.createElement("option"); option.value = group.groupId; option.text = group.bizGroupName; selectObj.add(option); } if (groups.length > 0 && selectObj.options.length > 1) selectObj.selectedIndex = 1; } } } $('#transferBtn').on('click', function() { if ($(this).hasClass('on')) { if (!_cc.getIsConnected()) { console.log('请先上线.'); return; } var transferArea = document.getElementById("transfer_area"); if (transferArea.style.display === "block") { transferArea.style.display = "none"; _cc.unSubscribeAgentList(); $("#doTransferBtn").hide(); $("#doConsultationBtn").hide(); } else { transferArea.style.display = "block"; populateGroupIdOptions(); $("#doTransferBtn").show(); $("#doConsultationBtn").hide(); } } }); $("#stopCallWait").hide(); $("#transferCallWait").hide(); $("#doConsultationBtn").hide(); $('#consultationBtn').on('click', function() { if ($(this).hasClass('on')) { if (!_cc.getIsConnected()) { console.log('请先上线.'); return; } var transferArea = document.getElementById("transfer_area"); if (transferArea.style.display === "block") { transferArea.style.display = "none"; _cc.unSubscribeAgentList(); $("#doConsultationBtn").hide(); $("#doTransferBtn").hide(); } else { transferArea.style.display = "block"; populateGroupIdOptions(); $("#doConsultationBtn").show(); $("#doTransferBtn").hide(); } } }); $('#onLineBtn').on('click', function() { if ($(this).hasClass('on')) { if (_cc.getIsConnected()) _cc.disconnect(); else _cc.connect(); } else alert('当前不允许签出!'); }); $('#resetStatus').on('click', function() { window.onbeforeunload = null; location.reload(); }); $("#ccphoneNumber").keydown(function(e) { if (e.which === 13) { var destPhone = $.trim($("#ccphoneNumber").val()); var videoLevel = document.getElementById("videoLevelSelect").value; var callType = document.forms[0].callType.value; _cc.call(destPhone, callType, videoLevel); return false; } }); $(document).keyup(function(e) { if (e.which === 27) { console.log('按下了ESC键, 即将发送挂机指令.'); if (_cc.getIsConnected()) { if (_cc.callConfig["useDefaultUi"]) { if ($('#hangUpBtn').hasClass('on')) _cc.hangup(); } else _cc.hangup(); } } }); }; this.holdCall = function() { this.sendMsg({ action: "callHold", body: { cmd: "hold", args: {} } }); }; this.unHoldCall = function() { this.sendMsg({ action: "callHold", body: { cmd: "unhold", args: {} } }); }; this.subscribeAgentList = function() { this.sendMsg({ action: "pollAgentList", body: { cmd: "subscribe", args: {} } }); _cc.subscribeAgentListStarted = true; }; this.unSubscribeAgentList = function() { if (_cc.subscribeAgentListStarted) { _cc.subscribeAgentListStarted = false; _cc.callConfig.agentList = null; this.sendMsg({ action: "pollAgentList", body: { cmd: "unSubscribe", args: {} } }); } }; // 会议相关方法(保持不变) this.conferenceStartBtnUI = function(customerName) { var callType = document.getElementById("conf_call_type").value; var confTemplate = document.getElementById("conf_template").value; var layOut = document.getElementById("conf_layout").value; _cc.setStatusBusy(); $("#callBtn").removeClass('on'); $("#setFree").removeClass('on'); $("#onLineBtn").removeClass('on'); document.getElementById("startConference").setAttribute("disabled", "true"); if (_cc.getCallConnected()) { let tips = "是否把当前通话转换为会议 ?"; if (confirm(tips)) _cc.transferToConference(layOut, confTemplate, callType, customerName); else document.getElementById("startConference").removeAttribute("disabled"); } else _cc.conferenceStart(layOut, confTemplate, callType); }; this.conferenceAddMemberBtnUI = function(reInvite, memberPhoneParam, memberNameParam) { var memberName = "", memberPhone = "", memberCallType = $.trim(document.getElementById("member_call_type").value), memberVideoLevel = $.trim(document.getElementById("member_video_level").value); if (reInvite === 0) { memberName = $("#member_name").val(); memberPhone = $("#member_phone").val(); if (memberName.length === 0 || $.trim(memberName) === "") { alert('请填写参会者姓名!'); return; } if (memberPhone.length === 0 || $.trim(memberPhone) === "") { alert('请填写参会者手机号!'); return; } memberName = $.trim(memberName); memberPhone = $.trim(memberPhone); var templateObj = document.getElementById("conf_member_template"); if (document.getElementById("conf_member_" + memberPhone) != null) { alert('会议成员已经存在,请不要重复添加!'); return; } var memberHtmlItem = templateObj.innerHTML; memberHtmlItem = memberHtmlItem.replace(new RegExp("{member_name}", "gm"), memberName); memberHtmlItem = memberHtmlItem.replace(new RegExp("{member_phone}", "gm"), memberPhone); memberHtmlItem = memberHtmlItem.replace(new RegExp("{member_status}", "gm"), "即将呼叫"); var li = document.createElement("li"); li.setAttribute("id", "conf_member_" + memberPhone); li.setAttribute("class", "conf_member_item_row"); li.innerHTML = memberHtmlItem; _cc.insertAfter(li, templateObj); $("#member_name").val(''); $("#member_phone").val(''); } else { memberName = memberNameParam; memberPhone = memberPhoneParam; } var memberItemId = "#conf_member_" + memberPhone; $(".conf_mute", $(memberItemId)).find("img").hide(); $(".conf_vmute", $(memberItemId)).find("img").hide(); $(".conf_re_invite", $(memberItemId)).hide(); $(".conf_status", $(memberItemId)).html("即将呼叫"); _cc.conferenceAddMembers([{ "name": memberName, "phone": memberPhone, "callType": memberCallType, "videoLevel": memberVideoLevel }]); }; this.conferenceAddMemberFromExistCall = function(memberName, memberPhone) { if (memberName.length === 0 || $.trim(memberName) === "") { alert('请填写参会者姓名!'); return; } if (memberPhone.length === 0 || $.trim(memberPhone) === "") { alert('请填写参会者手机号!'); return; } memberName = $.trim(memberName); memberPhone = $.trim(memberPhone); var templateObj = document.getElementById("conf_member_template"); if (document.getElementById("conf_member_" + memberPhone) != null) { alert('会议成员已经存在,请不要重复添加!'); return; } var memberHtmlItem = templateObj.innerHTML; memberHtmlItem = memberHtmlItem.replace(new RegExp("{member_name}", "gm"), memberName); memberHtmlItem = memberHtmlItem.replace(new RegExp("{member_phone}", "gm"), memberPhone); memberHtmlItem = memberHtmlItem.replace(new RegExp("{member_status}", "gm"), "即将呼叫"); var li = document.createElement("li"); li.setAttribute("id", "conf_member_" + memberPhone); li.setAttribute("class", "conf_member_item_row"); li.innerHTML = memberHtmlItem; _cc.insertAfter(li, templateObj); var memberItemId = "#conf_member_" + memberPhone; $(".conf_mute", $(memberItemId)).find("img").show(); $(".conf_vmute", $(memberItemId)).find("img").show(); $(".conf_re_invite", $(memberItemId)).hide(); $(".conf_status", $(memberItemId)).html("通话中").css("color", "green"); }; this.transferToConference = function(layOut, confTemplate, callType, customerName) { console.log("try to transferToConference: ", layOut, confTemplate, callType, customerName); _cc.callControl("transferToConference", { "layOut": layOut, "callType": callType, "confTemplate": confTemplate, "customerName": customerName }); }; this.conferenceStart = function(layOut, confTemplate, callType) { this.sendMsg({ action: "conference", body: { method: "startconf", args: { layOut: layOut, callType: callType, confTemplate: confTemplate } } }); }; this.conferenceVMuteMember = function(members) { this.conferenceControl("vmute", typeof members === "string" ? [{ "phone": members }] : members); }; this.conferenceUnVMuteMember = function(members) { this.conferenceControl("unvmute", typeof members === "string" ? [{ "phone": members }] : members); }; this.conferenceMuteMember = function(members) { this.conferenceControl("mute", typeof members === "string" ? [{ "phone": members }] : members); }; this.conferenceUnMuteMember = function(members) { this.conferenceControl("unmute", typeof members === "string" ? [{ "phone": members }] : members); }; this.conferenceAddMembers = function(members) { this.conferenceControl("add", members); }; this.conferenceRemoveMembers = function(members) { if (typeof members === "string") { var memberItemObj = $("#conf_member_" + members); if ($(".conf_status", memberItemObj).text() === "通话中") this.conferenceControl("remove", [{ "phone": members }]); memberItemObj.remove(); } else this.conferenceControl("remove", members); }; this.conferenceEnd = function() { this.conferenceControl("endconf", []); }; this.conferenceControl = function(action, phoneList) { this.sendMsg({ action: "conference", body: { method: action, memberList: phoneList } }); }; this.callMonitorDataPull = function() { this.sendMsg({ action: "monitorData", body: {} }); }; this.inboundCallQueuePull = function() { this.sendMsg({ action: "inboundMonitorData", body: {} }); }; this.monitorControl = function(action, argsObject) { this.sendMsg({ action: "callMonitor", body: { cmd: action, args: argsObject } }); }; this.callMonitorStart = function(callId) { if (callId == null || callId.length === 0) { console.log('请提供待监听电话的 callId !'); return; } if (!_cc.getIsConnected()) { console.log('请先上线.'); return; } this.monitorControl("startMonitoring", { 'callSpyId': callId }); }; this.callMonitorEnd = function() { if (!_cc.getIsConnected()) { console.log('请先上线.'); return; } this.monitorControl("endMonitoring", {}); }; } export default ccPhoneBarSocket;