UNPKG

jssip-emicnet

Version:

jssip wrapper, use for emicnet callcenter

997 lines (968 loc) 127 kB
import Logger from '../lib/Logger' import Phone from './phone' import creatHtml from './html' import config from '../lib/config' const logger = new Logger('phonebar:ui') ; (function (global, doc) { class Ephone { constructor() { //定义Ephone的属性 this.options = { width: '490px', height: '50px', background: '', callBackground: ['#1E59B9', '#19C583', '#FF6754', '#E6A23C'], drop: true } this.closeTimer = null this.watchTimer = null this.outTimer = null this.titleTimer = null this.recordsTotal = null // 某组坐席总数 滚动加载时使用 this.callintype = 2 // 模式 this.callType = 0 // type = 1 外线拨号 type = 2 回拨 type = 3 内线互拨 this._ccNumber = undefined //ccNumber this.socket_uri = config.devcm this.consultParams = {} this.monitorState = { isMonitor: false, // 标记是否在监听状态 selectedSeat: null, // 被选中的坐席 selectedGid: undefined // 被选中的技能组 id } } init(targetID, options) { // var ccNumber = localStorage.getItem("ccNumber") var userData = localStorage.userData ? JSON.parse(localStorage.userData) : false var toolbar = document.createElement('div') var target = doc.querySelector(targetID) || doc.querySelector('body') if (!!window.ActiveXObject || 'ActiveXObject' in window) { target.innerHTML = "<h1 style='text-align:center'>请使用非 IE 浏览器!!!</h1>" return } //初始化配置 if (typeof options == 'object' && options) { this.options.width = options.width ? options.width : this.options.width this.options.height = options.height ? options.height : this.options.height this.options.background = options.background ? options.background : this.options.background this.options.drop = options.drop != undefined ? options.drop : this.options.drop this.options.callBackground = options.callBackground && Array.isArray(options.callBackground) ? options.callBackground : this.options.callBackground } //增加拖拽功能 if (this.options.drop) { this._addDrop(toolbar, target) } //html toolbar.style.maxWidth = this.options.width toolbar.style.boxShadow = '0px 1px 15px ' + '#c3c3c3' toolbar.style.zIndex = 999 toolbar.style.background = this.options.background toolbar.id = 'PHONE-DROP' toolbar.innerHTML = creatHtml(this.options) target.appendChild(toolbar) if (localStorage.refresh) { alert('您可能已打开一个页面,同时打开多个页面会造成通话不正常') /* * 经过试验,强制关闭 Safari浏览器 beforeunload不会被执行 * Chrome 75版本可以,但我们确实也碰到refresh没被清除的情况,暂时没找到复现规律 * 所以保险点就是清掉refresh。 * 如果是异常造成refresh没清掉,下次再打开就可以了 * 如果确实已打开一个tab,清掉了refresh,唯一问题是再打开一次,不再提醒了。 */ localStorage.removeItem('refresh') window.open( 'https://www.yuque.com/yimi/phonebar/welcome-to-lark', '_self' ) return } else { localStorage.refresh = '1' } //添加DOM事件处理 this.eventListener() // this._hideAllMode() // https://developer.mozilla.org/en-US/docs/Web/API/Window/beforeunload_event window.addEventListener('beforeunload', e => { Phone.terminate(this.ccNumber) if (Phone.kefuStatus == 0 && localStorage.getItem('userData')) localStorage.removeItem('userData') localStorage.removeItem('refresh') e.returnValue = '离开取消会话' }) // 初始位置 功能按钮位置 配置项可配 if (userData && userData.setting) { this.setTogglePosition(userData.setting) } } eventListener() { var stopProList = [] var that = this //回调函数才需要that=this,或者箭头函数 var targetId = '' // 技能组id var timer = null var page = 1 // 记录当前页数 var checked = 0 // 仅查可转接坐席是否勾选 0 未勾选 1 已勾选 var gid var tranNumber // 三方 var threeway = 0 //工具条元素 let actionButtons = doc.querySelectorAll( '#EphoneBar li[data-phone-key]' ) let registerBtn = doc.querySelector( "#EphoneBar li[data-phone-type='register']" ) //登陆 var holdBtn = doc.querySelector( "#EphoneBar li[data-phone-type='hold']" ) var unHoldBtn = doc.querySelector( "#EphoneBar li[data-phone-type='unhold']" ) let planePageBtn = doc.querySelector( "#PHONE-LEFT-STATUS>div[data-type='planePage']>div" ) //拨号面板按钮 let kefuStatus = registerBtn.querySelector( "span[data-type='toggle']" ) //注册按钮显示状态 //弹框元素 let loginTarget = doc.querySelector('#PHONE-ENTRY-LOGIN[data-hide]') //登陆页面 let selectGroupTarget = doc.querySelector( '#PHONE-ENTRY-SELECTGROUP[data-hide]' ) //登陆选择技能组 let toggleTarget = doc.querySelector( '#PHONE-ENTRY-TOGGLE[data-hide]' ) //切换状态 let settingTarget = doc.querySelector( '#PHONE-ENTRY-SETTING[data-hide]' ) //设置页面 let panel = doc.querySelector('#PHONE-ENTRY-PANEL[data-hide]') //拨号面板 let switchPage = doc.querySelector('#PHONE-ENTRY-SWITCH') //转接页面 var incomingStatus = doc.querySelector( "#PHONE-LEFT-STATUS>div[data-type='incomingStatus']>div" ) //来电 //loginTarget 登陆页面元素 let Logininputs = doc.querySelectorAll( '#PHONE-ENTRY-LOGIN[data-hide] input[data-type]' ) let switchnumber = Logininputs[0] //总机号 let seatnumber = Logininputs[1] //分机号 let password = Logininputs[2] //密码 let modelnumber = Logininputs[3] //回拨话机/sip话机 let error = loginTarget.querySelector("span[data-type='error']") // 错误提示 var statusBtn = loginTarget.querySelector( "[data-type='loginStatus']" ) var status_text = statusBtn.querySelector('span') // 示忙/示闲 let modelSel = loginTarget.querySelectorAll( "li input[name='callintype']" ) let modelLi = loginTarget.querySelector("li[data-type='model']") //selectGroupTarget 登陆选组元素 let selectGroupTargetConfirm = selectGroupTarget.querySelector( "button[data-type='confirm']" ) let busyStatus = toggleTarget.querySelector("li[data-state_id='2']") // settingTarget //设置页面元素 let moveBtn = settingTarget.querySelector("input[value='51']") let fixBtn = settingTarget.querySelector("input[value='52']") let wsUrl = settingTarget.querySelector("input[data-type='wsUrl']") let settingError = settingTarget.querySelector( "span[data-type='error']" ) //panel 快速拨号元素 let panelInput = panel.querySelector("input[data-type='input']") //手机号输入框 // switchPage转接页元素 let p = switchPage.querySelector("div[data-type='select_text']") let errorTitle = switchPage.querySelector("span[data-type='error']") let checkInp = switchPage.querySelector("input[type='checkbox']") let optionInp = switchPage.querySelectorAll('option') let kefuList = switchPage.querySelector("ul[data-type='kefuList']") //坐席 // monitorPage 页面的相关元素 const MonitorPage = doc.querySelector('#PHONE-ENTRY-MONITOR') //监控页面 const MonitorPage_p = MonitorPage.querySelector( "div[data-type='select_text']" ) const MonitorPage_error = MonitorPage.querySelector( "span[data-type='error']" ) const MonitorPage_checkbox = MonitorPage.querySelector( "input[type='checkbox']" ) const MonitorPage_options = MonitorPage.querySelector('option') const MonitorPage_kefuList = MonitorPage.querySelector( "ul[data-type='kefuList']" ) let MonitorGroupTarget = doc.querySelector( '#PHONE-ENTRY-MONITOR[data-hide]' ) //incomingStatus来电呼入 let incomingS = incomingStatus.querySelector( "span[data-type='status']" ) let incomingA = incomingStatus.querySelector( "button[data-type='answer']" ) let title = doc.querySelector( "#PHONE-LEFT-STATUS div[data-type='pattern']>div" ) //移动模式提示 // 咨询按钮的 dom // eslint-disable-next-line prettier/prettier const consultBtn = doc.querySelector( "li[data-phone-type='consult']" ) const consultCancelBtn = doc.querySelector( "li[data-phone-type='consult-cancel']" ) const consultPage = doc.querySelector('#PHONE-ENTRY-CONSULT') // eslint-disable-next-line prettier/prettier const consultTabs = consultPage.querySelectorAll( 'li[class="consult-tab-header-item"]' ) // 咨询的 tab 点击 Array.from(consultTabs).forEach(tab => { tab.onclick = event => { event.stopPropagation() Array.from(consultTabs).forEach(tab => { tab.classList.remove('active') }) tab.classList.add('active') const type = tab.dataset.type switch (type) { case 'consult-to-seat': // 获取咨询的坐席 // eslint-disable-next-line prettier/prettier that.getGroupMembers( { gid: '' }, null, 'consultPage' ) break case 'consult-to-group': // 获取咨询的技能组 that.getGroups('consultPage') break case 'consult-to-outline': // 获取咨询的外线 that.getOutlineNumbres() break default: // 默认获取坐席 // eslint-disable-next-line prettier/prettier that.getGroupMembers( { gid: '' }, null, 'consultPage' ) } } }) //自动登陆 if (localStorage && localStorage.userData) { this.register() } // 聚焦 错误提示隐藏 // Array.from(Logininputs).forEach(v => { // v.onfocus = () => { error.innerText = '' } // }) //工具条点击事件 Array.from(actionButtons).forEach(v => { stopProList.push(v) v.onclick = async function (e) { e.stopPropagation() //this here is v so we can't use arrow function. //BUT I really hate coding like this!! var type = this.getAttribute('data-phone-type') var key = this.getAttribute('data-phone-key') try { JSON.parse(localStorage.userData) } catch (err) { if (err) { if (Phone.kefuStatus > 0) alert('请重新登录') that.sethighlight('register setting', true) kefuStatus.dataset.hide = '0' Phone.logout() } } if (this.classList.contains('gray')) return let userData = localStorage.userData ? JSON.parse(localStorage.userData) : undefined // const cc_number = that.monitorState?.selectedSeat?.cc_number; const cc_number = that.monitorState && that.monitorState.selectedSeat && that.monitorState.selectedSeat.cc_number; const monitor_uid = that.monitorState && that.monitorState.selectedSeat && that.monitorState.selectedSeat.monitor_uid; switch (key) { case '0': //注册,这里主要是续注册处理,要优化 logger.logDB(logger.debug, `当前状态:${Phone.kefuStatus}`) if (Phone.kefuStatus > 0) { userData = JSON.parse(localStorage.userData) // //状态 退出空闲忙碌元素 let statusBtns = toggleTarget.querySelector( "li[data-type='state']" ) Array.from(statusBtns).forEach(v => { v.dataset.hide = userData.seatMode == 1 ? '0' : '1' }) // leisureStatus.dataset.hide = busyStatus.dataset.hide = // userData.seatMode == 1 ? '0' : '1' that.setDisplayNone('statusPage') } else { that.setDisplayNone('loginPage') } break case '1': // 快速呼叫 挂断 保持 恢复 if (type == 'open') { that.setDisplayNone('fastPage') } if (type == 'terminate') { Phone.terminate(that.ccNumber) } if (type == 'hold') { // Phone.hold(that.ccNumber) //TODO MPTY call test 借用hold threeway += 1 logger.logDB(logger.debug, '借用hold调试三方通话') if (threeway % 2 == 1) { Phone.threewayCall({ ccNumber: that.ccNumber, type: 3, callee: '95555' }) } else { Phone.threewayCall({ ccNumber: that.ccNumber, type: 4 }) } } if (type == 'unhold') { Phone.unhold(that.ccNumber) } break case '2': // 转接 var hide = doc.querySelector( '#PHONE-ENTRY-SWITCH[data-hide]' ).dataset.hide if (hide == 0) { // 置空可转接座席选择框 checkInp.checked = false checked = 0 // 获取技能组 that.getGroups() // 获取坐席 getGroupMembers userData = localStorage.userData ? JSON.parse(localStorage.userData) : {} gid = userData.loginGid ? userData.loginGid : -1 that.getGroupMembers({ gid: gid }) // 获取技能组状态 getMemberCallStates // var res3 = await Phone.webApiHandler("getMemberCallStates", webParam) // console.log({ getMemberCallStates: res3 }) } that.setDisplayNone('switchPage') break case '4': //设置 that.setDisplayNone('setting') wsUrl.value = that.socket_uri if (userData && Phone.kefuStatus > 0) { userData.seatMode == '1' ? (moveBtn.checked = true) : (fixBtn.checked = true) moveBtn.disabled = fixBtn.disabled = false wsUrl.disabled = true } else { wsUrl.disabled = false moveBtn.disabled = fixBtn.disabled = fixBtn.checked = true } break case '5': // 监控的 switch (type) { case 'consult': // 咨询 that.setDisplayNone('consultPage') break case 'monitor_show': // 唤出页面之前应该先去获取可以被监听的坐席列表 if ( doc.querySelector( '#PHONE-ENTRY-MONITOR[data-hide]' ).dataset.hide == 0 ) { // 调用接口 获取可被监听的列表 await that.getMonitorGroups() await that.getMonitorGroupSeats('') that.setDisplayNone('monitorPage') } break case 'monitor_terminate': // 结束监听 // op_code 1 监听 2 强插 3 强拆/结束监听 if (!that.monitorState.isMonitor && !cc_number && !monitor_uid) { return } await that.monitorTerminate(cc_number, monitor_uid) break case 'monitor_insert': // 强插 if (!that.monitorState.isMonitor && !cc_number && !monitor_uid) { return } await that.monitorInsert(cc_number, monitor_uid) break case 'monitor_remove': // 强拆 if (!that.monitorState.isMonitor && !cc_number && !monitor_uid) { return } await that.monitorRemove(cc_number, monitor_uid) break case 'monitor_intercept': // 拦截 if (!that.monitorState.isMonitor && !cc_number && !monitor_uid) { return } await that.monitorIntercept(cc_number, monitor_uid) break } break case '6': // 取消咨询 that.consultParams.type = 4 await Phone.consultCall(that.consultParams) that.consultParams = {} consultBtn.dataset.hide = '1' consultCancelBtn.dataset.hide = '0' that.sethighlight('terminal,consult', true) break case '7': // 静音的 if (type === 'mute_start') { // 请求静音的逻辑 that.muteStart(that.ccNumber) } else { // 取消静音的逻辑 that.muteRemove(that.ccNumber) } break default: break } } }) //话机模式 切换 Array.from(modelSel).forEach(v => { v.onchange = function () { if (this.value != '2') { modelLi.dataset.hide = '1' modelLi.querySelectorAll('span').forEach(sv => { sv.dataset.hide = sv.dataset.type == this.value ? '1' : '0' }) modelnumber.value = '' modelnumber.oninput = null modelnumber.oninput = function () { v.value == '5' ? (this.value = this.value.replace(/\D/g, '')) : (this.value = this.value.replace( /[^0-9A-Z]/g, '' )) this.value.length > 15 ? (this.value = this.value.substr(0, 14)) : this.value } } else { modelLi.dataset.hide = '0' } } }) //loginTarget 登陆页面 loginTarget.onclick = async function (e) { e.stopPropagation() var target = e.target || e.srcElement that._closePage(target, loginTarget) if ( target.dataset.type == 'loginStatus' || target.parentNode.dataset.type == 'loginStatus' ) { var hasBusyClass = statusBtn.classList.contains('busy') hasBusyClass ? statusBtn.classList.remove('busy') : statusBtn.classList.add('busy') status_text.innerText = hasBusyClass ? '示闲' : '示忙' let preferredStatus = hasBusyClass ? '1' : '2' localStorage.setItem('preferredStatus', preferredStatus) } if (target.dataset.type == 'login') { var checkedModel = loginTarget.querySelector( "li input[name='callintype']:checked" ).value //https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/radio //https://developer.mozilla.org/en-US/docs/Web/API/DOMString //所以返回是string, 我们再转成 int that.callintype = +checkedModel if (switchnumber.value.length == 0) return that.showError( switchnumber, error, '请输入总机号码' ) if (seatnumber.value.length == 0) return that.showError( seatnumber, error, '请输入分机号码' ) if ( seatnumber.value.length < 4 || seatnumber.value.length > 6 ) return that.showError( seatnumber, error, '分机号码错误,请重新输入' ) if (password.value.length == 0) return that.showError(password, error, '请输入密码') if (checkedModel != '2' && modelnumber.value.length == '') return that.showError( modelnumber, error, '请输入话机号码' ) if (target.dataset.disabled == '0') return target.dataset.disabled = '0' let req = { un: seatnumber.value, pwd: password.value, switchnumber: switchnumber.value, callintype: that.callintype, phoneNumber: that.callintype != 2 ? modelnumber.value : undefined } if (that.socket_uri.startsWith('ws')) { logger.logDB(logger.debug, `${that.socket_uri} 一个小后门`) } if (that.socket_uri.startsWith('http')) { req.backend = that.socket_uri } let result = await Phone.getUser2(req) if (result.code != 200) { logger.logDB(logger.debug, 'getUser2', { code: result.code, info: result.info }) target.dataset.disabled = '1' return that.showError(switchnumber, error, result.info) } logger.logDB(logger.debug, 'getUser2', { result: result.data }) // logger.logDB(logger.debug, { groupInfo: res.memberInfo.inGroups}) var groupInfo = JSON.parse(localStorage.userData).groupInfo if (groupInfo.length > 1) { loginTarget.dataset.hide = '0' selectGroupTarget.dataset.hide = '1' selectGroupTargetConfirm.dataset.disabled = 'true' var group = '' groupInfo.map((v, i) => { group += "<li title='" + v.name + "' data-eid='" + v.eid + "' data-id='" + v.id + "'>" + v.name + '</li>' }) doc.querySelector( "#PHONE-ENTRY-SELECTGROUP ul[data-type='groupList']" ).innerHTML = group target.dataset.disabled = '1' return } var params = { // switchnumber: switchnumber.value, // un: seatnumber.value, // pwd: password.value, gid: groupInfo[0] ? groupInfo[0].id : 0, //没有技能组是异常情况,先暂时传 0 callintype: that.callintype, // socketUri: that.socket_uri, // remoteAudio: 'peeraudio', number: modelnumber.value } that.register(params, target) } } // 切换坐席状态、退出 toggleTarget.addEventListener('click', function (e) { e.stopPropagation() var target = e.target || e.srcElement var userData = localStorage.getItem('userData') ? JSON.parse(localStorage.getItem('userData')) : false var seatMode = userData.seatMode const type = target.dataset.type if (type === 'state') { const state_code = target.dataset.state_code const state_id = target.dataset.state_id if (+state_id === 2) { // 示闲 if (seatMode == 1 || that.ccNumber) return Phone.changeStaus({ state_code }) } else { if (seatMode == 1) return if ( that.ccNumber && busyStatus.dataset.disabled != '0' ) { if (busyStatus.dataset.status == '2') { Phone.preSetStatus({ ccNumber: that.ccNumber }) } else { Phone.preSetStatusCancle({ ccNumber: that.ccNumber }) } busyStatus.dataset.disabled = '0' logger.logDB(logger.debug, '通话中修改状态', busyStatus.dataset.status ) } else { // Phone.changeStaus('2') Phone.changeStaus({ state_code }) } } } if ( target.dataset.type == 'logout' || target.parentNode.dataset.type == 'logout' || target.parentNode.parentNode.dataset.type == 'logout' ) { // 退出 if ( target.dataset.disabled == 'true' || target.parentNode.dataset.disabled == 'true' || target.parentNode.parentNode.dataset.disabled == 'true' || that.ccNumber ) return //有通话时候不能退出 that.logoff() } toggleTarget.dataset.hide = '0' that.setArrowNone() }) // PHONE-ENTRY-SETTING 更改设置 settingTarget.onclick = function (e) { e.stopPropagation() var target = e.target || e.srcElement that._closePage(target, settingTarget) if (target.dataset.type == 'confirm') { var userData = localStorage.userData ? JSON.parse(localStorage.userData) : undefined var patternRadioValue = settingTarget .querySelector( "input[type='radio'][name='pattern']:checked" ) .getAttribute('value') var postionRadioValue = settingTarget .querySelector( "input[type='radio'][name='postionButton']:checked" ) .getAttribute('value') var pattern = patternRadioValue == '51' ? 1 : 2 var setting = { postion: postionRadioValue } //模式 if (userData && Phone.kefuStatus > 0) { userData.setting = setting localStorage.setItem( 'userData', JSON.stringify(userData) ) if (userData.seatMode != pattern) { Phone.setSeatMode(patternRadioValue) } } else { var reg = new RegExp( '(ws|wss)://[-A-Za-z0-9+&@#/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|]' ) var reg2 = new RegExp('(http|https)://') //设置 socket_uri 值 if (reg.test(wsUrl.value) || reg2.test(wsUrl.value)) that.socket_uri = wsUrl.value else return that.showError( wsUrl, settingError, '请输入正确的wss地址' ) } //定位 that.setTogglePosition(setting) that.setArrowNone() settingTarget.dataset.hide = 0 } } // PHONE-ENTRY-SELECTGROUP 选择技能组 selectGroupTarget.onclick = function (e) { e.stopPropagation() var target = e.target || e.srcElement that._closePage(target, selectGroupTarget) if (target.parentNode.dataset.type == 'groupList') { that._selectedUI(target, 'group') targetId += target.dataset.id + ',' selectGroupTargetConfirm.dataset.disabled = '1' logger.logDB(logger.debug, 'groupList:id', targetId) } if (target.dataset.type == 'confirm') { if ( target.dataset.disabled == '0' || target.dataset.disabled == 'true' ) return target.dataset.disabled = '0' // var len = document.querySelectorAll( // "#PHONE-ENTRY-SELECTGROUP ul[data-type='groupList']>li.selected" // ).length // if (len < 1) return var userData = JSON.parse(localStorage.userData) var params = { // switchnumber: userData.switchNumber, // un: userData.userInfo.number, // pwd: userData.pwd, gid: targetId.slice(0, -1), callintype: that.callintype // socketUri: that.socket_uri, // remoteAudio: 'peeraudio' } targetId = '' // 传入 sip 话机号 if (+params.callintype !== 2) { params.number = userData.device_number || modelnumber.value || undefined } that.register(params, target) } } // PHONE-ENTRY-PANEL 快速呼叫 panel.onclick = function (e) { e.stopPropagation() var target = e.target || e.srcElement that._closePage(target, panel) if (target.dataset.type == 'call') { //拨号 that.callout(panelInput) } } panelInput.onkeydown = function (e) { if (e.keyCode == '13') { that.callout(panelInput) } } //接听 incomingA.onclick = function (e) { e.stopPropagation() if (this.dataset.disabled == '0') return if (this.closeTimer) global.clearTimeout(this.closeTimer) this.dataset.disabled = '0' incomingS.innerHTML = "呼叫中<span class='dotting'></span>" Phone.answerPBXCall(that.ccNumber) } // PHONE-ENTRY-SWITCH 转接 switchPage.onclick = function (e) { e.stopPropagation() var target = e.target || e.srcElement var groupUl = switchPage.querySelector('ul[data-type="group"]') that._closePage(target, switchPage) if ( target.parentNode.dataset.type == 'select' || target.dataset.type == 'select' || target.classList[0] == 'arrow' ) { groupUl.dataset.hide = groupUl.dataset.hide == '1' ? '0' : '1' } else { groupUl.dataset.hide = '0' } if (target.parentNode.dataset.type == 'group') { p.innerText = target.innerText groupUl.dataset.hide = '0' logger.logDB(logger.debug, target.dataset.id) gid = target.dataset.id ? target.dataset.id : -1 page = 1 // 获取某组坐席 getGroupMembers let webParam = { gid } if (checked) webParam.searchServiceControl = checked that.getGroupMembers(webParam) } if (target.dataset.type == 'checkbox') { page = 1 logger.logDB(logger.debug, target.checked) let userData = JSON.parse(localStorage.userData) //TODO 转接需要测试 let webParam = { gid: gid || userData.loginGid } target.checked ? (webParam.searchServiceControl = checked = 1) : (checked = 0) that.getGroupMembers(webParam) } if (target.parentNode.dataset.type == 'kefuList') { if ( target.dataset.status == 'offLine' || target.dataset.status == 'busy' ) return that._selectedUI(target) gid = target.dataset.gids tranNumber = target.dataset.number } if ( target.dataset.type == 'transfer' && target.dataset.disabled != '0' && gid && tranNumber && target.dataset.disabled != 'true' ) { if (!that.ccNumber) return logger.logDB(logger.debug, '正在转接...', { gid, tranNumber, ccNumber: that.ccNumber }) target.dataset.disabled = '0' errorTitle.style.color = '#19C583' errorTitle.innerText = '正在转接,请稍后...' that.sethighlight('all', true) Phone.transferPBXCall( gid, tranNumber, that.ccNumber, function (res) { if (res.type == 'transferCallFaild') { switchPage.dataset.hide = '1' errorTitle.style.color = '#FD3D39' errorTitle.innerText = '电话转接失败' if (timer) global.clearTimeout(timer) timer = global.setTimeout(() => { errorTitle.innerText = '' that.sethighlight( 'terminate,hold,unhold,switch', true ) }, 2000) } else { errorTitle.innerText = '电话转接成功' if (timer) global.clearTimeout(timer) timer = global.setTimeout(() => { errorTitle.innerText = '' switchPage.dataset.hide = '0' }, 2000) } target.dataset.disabled = '1' } ) } } //滚动加载坐席 kefuList.onscroll = function (e) { var target = e.target || e.srcElement var pageTotal = Math.ceil(parseInt(that.recordsTotal) / 20) if ( target.scrollTop >= target.scrollHeight - target.offsetHeight ) { if (page == pageTotal) return var webParam = { gid, seat_page: pageTotal } that.getGroupMembers(webParam, true) page++ } } // 点击监听按钮触发的逻辑 MonitorPage.onclick = async function (e) { e.stopPropagation() const target = e.target || e.srcElement var groupsUl = MonitorPage.querySelector( 'ul[data-type="group"]' ) that._closePage(target, MonitorGroupTarget) // 展示可监听的技能组 if ( target.parentNode.dataset.type == 'select' || target.dataset.type == 'select' || target.classList[0] == 'arrow' ) { groupsUl.dataset.hide = groupsUl.dataset.hide == '1' ? '0' : '1' } else { groupsUl.dataset.hide = '0' } // 处理可监听技能组 if (target.parentNode.dataset.type == 'group') { MonitorPage_p.innerText = target.innerText groupsUl.dataset.hide = '0' logger.logDB(logger.debug, target.dataset.id) that.monitorState.selectedGid = target.dataset.id ? target.dataset.id : ' ' await that.getMonitorGroupSeats( that.monitorState.selectedGid ) } // 处理仅查看可监听坐席按钮 if (target.dataset.type == 'checkbox') { logger.logDB(logger.debug, target.checked) let userData = JSON.parse(localStorage.userData) let groupId = that.monitorState.selectedGid || userData.loginGid let state_id = undefined if (target.checked) { state_id = 4 } else { state_id = '' } await that.getMonitorGroupSeats(groupId, state_id) } // 处理坐席被选中 if (target.parentNode.dataset.type == 'kefuList') { // 修改当前被选中的坐席,保存数据用于调用监听接口 if (target.dataset.status_code != 4 || !target.dataset.cc) { return } that.monitorState.selectedSeat = { cc_number: target.dataset.cc, monitor_uid: target.dataset.uid } that._selectedUI(target) } // 确认按钮 if ( target.dataset.type == 'monitorIcon' && that.monitorState.selectedSeat ) { doc.querySelector('#PHONE-ENTRY-MONITOR').dataset.hide = 0 that.handleMonitorAbout(1) } // 取消按钮 if (target.dataset.type == 'close') { } } //拨号面板 var planePage = doc.querySelector('#PHONE-PANEL') var input = planePage.querySelector("input[data-type='input']") planePageBtn.onclick = function (e) { e.stopPropagation() var hide = planePage.dataset.hide planePage.