Newer
Older
CallCenterFront / src / layout / components / ivr / ivrBar.vue
<template>
  <div class="ivr-div">
    <ivr-btn :show="!signStatus" icon="qianru" @click="login">签入</ivr-btn>
    <ivr-btn :show="signStatus" icon="qianchu" @click="logout">签出</ivr-btn>
    <ivr-btn :show="!holdStatus" :disabled="(!signStatus)||(isInCall&&!holdStatus)" icon="baochi" @click="hold">保持</ivr-btn>
    <ivr-btn :show="isInCall&&holdStatus" :disabled="isInCall&&holdStatus" icon="baochi" @click="unhold">解除保持</ivr-btn>
    <ivr-btn :show="true" :disabled="(!signStatus)||(signStatus&&isInCall)" icon="hujiao" @click="dialIn">内呼</ivr-btn>
    <ivr-btn :show="true" :disabled="(!signStatus)||(signStatus&&isInCall)" icon="duiwaihujiao" @click="dialOut">外呼</ivr-btn>
    <ivr-btn :show="true" :disabled="!(isInCall&&isInComing)" icon="hujiao" @click="transferDial">转接</ivr-btn>
    <ivr-btn :disabled="!isInCall" icon="guaji" @click="hangup">挂机</ivr-btn>
    <ivr-btn :disabled="!(isInCall&&isInComing)" icon="hy" @click="meeting">会议</ivr-btn>
    <ivr-btn :show="!btnStatus.btnIdle" :disabled="btnStatus.btnIdle" icon="xiaoxiu" @click="onidle">退出示忙</ivr-btn>
    <ivr-btn :show="btnStatus.btnIdle" :disabled="btnStatus.btnBusy" icon="shimang" @click="onbusy">示忙</ivr-btn>
    <!--<ivr-btn :show="!btnStatus.btnQuitWorkState" :disabled="btnStatus.btnWorkState" icon="icon-work" @click="quitWorkState">退出工作态</ivr-btn>-->
    <!--<ivr-btn :show="!btnStatus.btnWorkState" :disabled="btnStatus.btnQuitWorkState" icon="icon-work" @click="goWorkState">进入工作态</ivr-btn>-->
    <div class="state-show">
      <div>来电号码:<span class="highlight">{{ number }}</span></div>
      <div>通话时间:<span class="highlight">{{ time | timeFilter }}</span><span class="highlight">{{ callStatus | windowTextFilter }}</span></div>
    </div>
    <!--软键盘-->
    <keyboard ref="keyboard" @call="dial"/>
    <!--选择分机-->
    <choose-exten ref="chooseexten" :exten-list="extenList" @finish="finishChooseExten"/>
    <!--选择坐席-->
    <choose-seats ref="chooseseats" :seat-list="extensionList" :self="nowExten" @call="dial"/>
    <!--外呼状态栏-->
    <el-dialog
      :title="statusText"
      :visible.sync="statusShow"
      width="30%"
      append-to-body>
      <span>{{ callStatus }}</span><span class="dot">...</span>
    </el-dialog>
    <!--新建事件页面-->
    <el-dialog
      :visible.sync="showAddCase"
      :close-on-click-modal="false"
      :close-on-press-escape="false"
      title="新建事件"
      width="1200px"
      custom-class="addcase-dialog"
      top="60px"
      append-to-body>
      <left-call-bar :tel="currentNumber" :hold-status="holdStatus" :callStatus="isInCall" @click="click"/>
      <div style="display: inline-block; width:1070px;">
        <create-case ref="addcase" @cancel="closeCreateDialog"/>
      </div>
    </el-dialog>
  </div>
</template>

<script>
import Keyboard from './keyboard'
import IvrBtn from './ivrBtn'
import CreateCase from '@/views/caseManage/createCase'
import { getToday } from '@/utils/dateutils'
import ChooseSeats from './chooseSeats'
import { getProject } from '@/utils/baseConfig'
import { seatReport } from '@/api/call'
import ChooseExten from './chooseExten'
import { mapState } from 'vuex'
import LeftCallBar from '../../../components/cti/LeftCallBar'

export default {
  name: 'IvrBar',
  components: { LeftCallBar, ChooseExten, ChooseSeats, CreateCase, IvrBtn, Keyboard },
  data() {
    return {
      debug: true, // 是否开启debug模式
      nowExten: '', // 当前使用的分机号
      extenList: [], // 待选分机列表
      agentName: this.$store.getters.seat, // 坐席号
      userId: this.$store.getters.id, // 用户id
      deptid: this.$store.getters.deptId, // 部门编号
      deptName: this.$store.getters.deptName, // 部门名称
      seatsList: [], // 坐席列表
      heartbeatInterval: 0, // 心跳interval
      heartbeatCount: 0, // 心跳heartbeat count
      heartbeatID: 0, // 心跳heartbeat id
      // webSocket: this.$store.state.ivr.websocket, // ws
      btnStatus: {
        btnLogin: false, // 签入
        btnLogOut: true, // 签出
        btnHold: true, // 保持
        btnUnhold: true, // 解除保持
        btnHangUp: true, // 挂机
        btnDialOut: true, // 外呼
        btnDialIn: true, // 内呼
        btnTransfer: true, // 转接
        btnBusy: true, // 示忙
        btnIdle: true, // 示闲
        btnMeeting: true, // 会议/三方通话
        btnWorkState: false, // 进入工作态
        btnQuitWorkState: true // 退出工作态
      },
      status: '', // 签入状态:已连接,已签入,未签入
      holdStatus: false, // 保持状态
      currentNumber: '', // 当前接入号码
      busy: '', // 坐席忙碌状态 :忙碌,'', 另一坐席已登录
      call: '', // 通话状态
      isInComing: false, // 是否来电
      // reg: '', // 话机状态
      number: '', // 通话号码
      diaStatus: '', // 拨号状态
      statusText: '', // 拨号状态标题
      statusDetailText: '', // 拨号状态详情
      statusShow: false, // 拨号的状态显示
      time: 0, // 来电计时,单位:秒
      timeInterval: null,
      threeWayPhone: '', // 三方通话电话号码
      ip: getProject().ws_ip, // websocket Ip
      port: getProject().ws_port, // webSocket Port
      showAddCase: false, // 是否显示新建案卷的弹窗
      notify: null // 来电通知
    }
  },
  computed: {
    ...mapState({
      exten: state => state.user.exten, // 用户绑定的分机号
      wsStatus: state => state.ivr.wsStatus,
      signStatus: state => state.ivr.signStatus, // 签入签出状态
      callStatus: state => state.ivr.callStatus,
      isInCall: state => state.ivr.isInCall,
      isInComming: state => state.ivr.isInComming,
      extensionList: state => state.ivr.extensionList,
      reg: state => state.ivr.reg,
      webSocket: state => state.ivr.webSocket,
      websocketObj: state => state.ivr.websocketObj
    }),
    timeFormat() { // 将时间格式化为时分秒样式
      const h = Math.floor(this.time / 3600)
      const m = Math.floor((this.time - h * 3600) / 60)
      const s = this.time - h * 3600 - m * 60
      const str = ('0' + h).slice(-2) + ':' + ('0' + m).slice(-2) + ':' + ('0' + s).slice(-2)
      return str
    }
  },
  filters: {
    // 呼叫时间过滤器
    timeFilter(val) {
      const h = Math.floor(val / 3600)
      const m = Math.floor((val - h * 3600) / 60)
      const s = val - h * 3600 - m * 60
      const str = ('0' + h).slice(-2) + ':' + ('0' + m).slice(-2) + ':' + ('0' + s).slice(-2)
      return str
    },
    // 弹窗内文字
    windowTextFilter(val) {
      if (val) {
        if (val === '呼入振铃') {
          return '呼入振铃'
        } else if (val === '外呼振铃' || val === '外呼准备' || val === '开始拨号') {
          return '呼叫中'
        } else if (val === '通话结束' || val === '空闲') {
          return '空闲中'
        } else {
          return '通话中'
        }
      } else {
        return '空闲中'
      }
    }
  },
  watch: {
    // busy(val) {
    //   // 忙碌状态为不忙时,示忙按钮允许点,示闲不允许
    //   if (val === '') {
    //     this.btnStatus.btnBusy = false
    //     this.btnStatus.btnIdle = true
    //   } else if (val === '忙碌') {
    //     this.btnStatus.btnBusy = true
    //     this.btnStatus.btnIdle = false
    //   }
    // },
    // wsStatus(val) { // 监控ws连接状态
    //   if (val) { // 如果wsStatus==true
    //     if (!this.signStatus) { // 如果还没有签入
    //       this.btnStatus.btnLogin = false
    //     } else {
    //       this.btnStatus.btnLogin = true
    //     }
    //   } else {
    //     this.btnStatus.btnLogin = true
    //   }
    // },
    signStatus(val) { // 监控签入状态
      console.log('sign status Change:' + val)
      if (val) { // 已签入
        this.onLoginSuccess()
      } else {
        this.logoutSuccess()
      }
    },
    callStatus(val) { // 通话状态
      console.log(val)
      if (val === '通话结束') { // 通话结束
        this.callOver()
      } else if (val === '外呼通话' || val === '呼入通话' || val === '自动外呼接入') {
        this.startCall()
      }
    },
    reg(val) {
      if (val === 'incomming answered') { // 接听呼入
        this.incommingAnswerd()
      } else if (val === 'holding') { // 状态为保持
        this.holdStatus = true
        this.btnStatus.btnHold = true
        this.btnStatus.btnUnhold = false
      } else if (val === 'unholding') { // 状态为解除保持
        this.holdStatus = false
        this.btnStatus.btnHold = false
        this.btnStatus.btnUnhold = true
      } else if (val === 'incomming ringing') { // 来电振铃
        this.notify = this.$notify({
          title: '未接来电',
          message: this.websocketObj.number,
          duration: 0
        })
      } else if (val === 'outbound ringing') { // 外呼振铃后的操作
        this.onOutBoundRinging()
      } else if (val === 'outbound answered') { // 外呼接通,允许保持,挂断
        this.onOutBoundAnswered()
      } else if (val === 'hangup') { // 挂断, 保持和解除保持不好用,其他均可
        this.btnStatus.btnDialOut = false
        this.btnStatus.btnDialIn = false
        this.btnStatus.btnHold = true
        this.btnStatus.btnUnhold = true
        this.btnStatus.btnBusy = false
        this.btnStatus.btnIdle = false
        this.btnStatus.btnMeeting = false
        this.btnStatus.btnHangUp = false
      }
    }
  },
  created() {
    this.initSocket()
  },
  methods: {
    // 初始化Socket,ok
    initSocket() {
      if ('WebSocket' in window) {
        console.log('您的浏览器支持Websocket!')
        this.$store.dispatch('initWebSocket')
      } else {
        this.$message.error('您的浏览器不支持Websocket!请更换浏览器!')
      }
    },
    // 坐席签入,ok
    login() {
      // 先判断websocket状态
      if (this.wsStatus) {
        // 如果没有绑定的分机号,需要先输入分机号,有分机号直接签入
        if (!this.exten) {
          this.$prompt('请输入分机号', '提示', {
            confirmButtonText: '确定',
            cancelButtonText: '取消'
          }).then(({ value }) => {
            this.nowExten = value
            this.doLogin()
          })
        } else {
          // 获取分机号,
          this.extenList = this.exten.split(',')
          if (this.extenList.length > 1) {
            // 打开选择分机号弹窗
            this.$refs.chooseexten.initDialog()
          } else {
            this.nowExten = this.exten
            this.doLogin()
          }
        }
      } else {
        this.$message.error('websocket连接状态异常,请刷新页面')
      }
    },
    // 结束选择分机号
    finishChooseExten(exten) {
      this.nowExten = exten
      this.doLogin()
    },
    // websocket执行签入操作
    doLogin() {
      // 传入分机号和坐席名称
      const { userId, deptid, deptName, nowExten, agentName } = this
      this.$store.dispatch('signin', { userId, deptid, deptName, nowExten, agentName })
    },
    // 签入成功后按钮的操作
    onLoginSuccess() {
      this.onidle()
      this.btnStatus.btnLogin = true // 签入按钮不显示
      this.btnStatus.btnLogOut = false
      this.btnStatus.btnDialOut = false
      this.btnStatus.btnDialIn = false
      this.btnStatus.btnHold = false
      this.btnStatus.btnUnhold = true
      this.btnStatus.btnBusy = false
      this.btnStatus.btnIdle = true // 置闲不显示
      this.btnStatus.btnMeeting = true
      this.btnStatus.btnHangUp = true
      this.btnStatus.btnTransfer = true // 转接不可用
      this.status = '已签入'
      this.call = '空闲'
      // 通知后台已签入
      this.sendRecord('1', '签入')
    },
    // 坐席签出
    logout() {
      this.$confirm(
        '确认签出吗?',
        '确认操作',
        {
          confirmButtonText: '确定',
          cancelButtonText: '取消',
          type: 'warning'
        }
      ).then(() => {
        this.$store.dispatch('signout')
      })
    },
    logoutSuccess() {
      this.status = '未签入'
      this.$message.success('签出成功')
      // 处理按钮状态
      this.btnStatus.btnLogin = false
      this.btnStatus.btnLogOut = true
      this.btnStatus.btnDialOut = true
      this.btnStatus.btnDialIn = true
      this.btnStatus.btnTransfer = true
      this.btnStatus.btnHold = true
      this.btnStatus.btnUnhold = true
      this.btnStatus.btnBusy = false
      this.btnStatus.btnIdle = true
      this.btnStatus.btnMeeting = true
      this.btnStatus.btnHangUp = true
      // 通知后台已签入
      this.sendRecord('1', '签出')
    },
    // 外呼,ok
    dialOut() {
      if (this.judgeStatus()) {
        if (!this.isInCall) {
          this.$refs.keyboard.initDialog('out')
        } else {
          this.$message.warning('已在通话中')
        }
      }
    },
    // 内呼,ok
    dialIn() {
      if (this.judgeStatus()) {
        // 如果在通话中,不允许内呼
        if (!this.isInCall) {
          this.getExtentionList()
          this.$refs.chooseseats.initDialog('in')
        } else {
          this.$message.warning('已在通话中')
        }
      }
    },
    // 转接,ok
    transferDial() {
      if (this.judgeStatus()) { // 判断坐席状态
        if (this.judgeCallStatus()) { // 判断通话状态
          if (this.isInComing) { // 判断是否外接电话
            this.$refs.chooseseats.initDialog('transfer')
          } else {
            this.$message.warning('只有外接电话才允许转接')
          }
        }
      }
    },
    // 获取已登录坐席列表,ok
    getExtentionList() {
      const params = {
        type: 'agent'
      }
      this.$store.dispatch('getExtensionList', params)
    },
    // 拨号
    dial(type, tel) {
      if (type === 'meeting') {
        this.goMeeting(tel)
      }
      if (type === 'out') {
        tel = '9' + tel
      }
      if (type === 'transfer') {
        this.goTransfer(tel)
      }
      this.statusShow = true
      this.statusText = '呼叫中...'
      this.statusDetailText = '呼叫中'
      this.$store.dispatch('dial', tel)
    },
    // 直接去转接
    goTransfer(tel) {
      this.$store.dispatch('transfer', tel)
    },
    // 示忙
    onbusy() {
      if (this.judgeStatus()) { // 已签入且不在通话中
        if (!this.isInCall) {
          this.$store.dispatch('onbusy')
          this.btnStatus.btnBusy = true
          this.btnStatus.btnIdle = false
          this.sendRecord('4', '置忙')
        }
      }
    },
    // 示闲
    onidle() {
      if (this.judgeStatus()) { // 已签入且不在通话中
        if (!this.isInCall) {
          this.$store.dispatch('onidle')
          this.btnStatus.btnBusy = false
          this.btnStatus.btnIdle = true
          this.sendRecord('4', '置闲')
        }
      }
    },
    // 挂机
    hangup() {
      if (this.judgeStatus()) { // 判断坐席状态,
        if (this.judgeCallStatus()) { // 判断通话状态
          this.$store.dispatch('hangup')
        }
      }
    },
    // 保持
    hold() {
      if (this.judgeStatus()) { // 判断坐席状态
        if (this.judgeCallStatus()) { // 判断通话状态
          this.$store.dispatch('hold')
          this.sendRecord('3', '进入保持')
        }
      }
    },
    // 解除保持
    unhold() {
      if (this.judgeStatus()) {
        this.$store.dispatch('unhold')
        this.sendRecord('3', '解除保持')
      }
    },
    // 通话开始
    startCall() {
      const that = this
      this.number = this.websocketObj.number
      // 开始通话后3s弹窗消失
      if (this.statusShow) {
        setTimeout(function() {
          that.statusShow = false
        }, 3000)
      }
      // 开启计时器
      this.time = 0
      this.timeInterval = setInterval(function() {
        that.time += 1
      }, 1000)
    },
    // 通话结束
    callOver() {
      this.btnStatus.btnTransfer = true
      this.btnStatus.btnHangUp = true
      // 结束计时器
      if (this.timeInterval) {
        this.$message.info('通话结束')
        clearInterval(this.timeInterval)
        this.timeInterval = null
        this.time = 0
        this.number = ''
      }
      // 如果还显示着弹窗,关闭弹窗
      if (this.statusShow) {
        this.statusShow = false
      }
      // 如果是来电,进入工作态
      // if (this.isInComing) {
      //   this.goWorkState()
      // }
    },
    // 处理接通来电之后
    incommingAnswerd() {
      const object = this.websocketObj
      this.isInComing = true
      if (this.notify) {
        this.notify.close() // 关闭来电提醒
      }
      // 处理按钮显示问题
      this.btnStatus.btnDialOut = true
      this.btnStatus.btnDialIn = true
      this.btnStatus.btnHold = false
      this.btnStatus.btnUnhold = true
      this.btnStatus.btnBusy = false
      this.btnStatus.btnIdle = true
      this.btnStatus.btnMeeting = true
      this.btnStatus.btnHangUp = false
      this.btnStatus.btnTransfer = false
      // 判断是不是内呼,内呼则不弹出新建工单页面
      // if (!this.judgeInsideNumber()) {
      // 弹出新建工单窗口
      this.showAddCase = true
      const data = {
        callid: object.callid,
        number: object.number,
        dialStartStamp: getToday('yyyy-MM-dd hh:mm:ss')
      }
      this.currentNumber = object.number
      console.log(data)
      const that = this
      setTimeout(function() {
        that.$refs['addcase'].initData(data)
      }, 500)
      // }
    },
    // 新建事件完毕调用
    closeCreateDialog() {
      this.showAddCase = false // 关闭弹窗
    },
    // 进入工作态
    goWorkState() {
      // this.$message.info('已进入工作态')
      this.btnStatus.btnWorkState = true
      this.btnStatus.btnQuitWorkState = false
      this.sendRecord('2', '进入工作态')
      this.onbusy() // 置忙
    },
    // 退出工作态
    quitWorkState() {
      // this.$message.info('已退出工作态')
      this.btnStatus.btnWorkState = false
      this.btnStatus.btnQuitWorkState = true
      this.sendRecord('2', '退出工作态')
      this.onidle() // 置闲
    },
    // 点击会议,打开会议弹窗
    meeting() {
      if (this.judgeStatus()) { // 判断坐席状态
        if (this.judgeCallStatus()) { // 判断通话状态
          if (this.isInComing) {
            this.$refs.chooseseats.initDialog('meeting')
          } else {
            this.$message.warning('只有外接电话才允许三方通话')
          }
        }
      }
    },
    // 开启三方通话
    goMeeting(tel) {
      this.$store.dispatch('meeting', tel)
    },
    // 外呼振铃的操作
    onOutBoundRinging() { // 除了挂断其他不允许点
      this.btnStatus.btnDialOut = true
      this.btnStatus.btnDialIn = true
      this.btnStatus.btnHold = true
      this.btnStatus.btnUnhold = true
      this.btnStatus.btnBusy = true
      this.btnStatus.btnIdle = true
      this.btnStatus.btnMeeting = true
      this.btnStatus.btnHangUp = false
    },
    // 外呼接听后的操作 , 允许保持,允许挂断,其余均不允许
    onOutBoundAnswered() {
      this.btnStatus.btnHold = false
      this.btnStatus.btnHangUp = false
      this.btnStatus.btnTransfer = true
      this.btnStatus.btnDialOut = true
      this.btnStatus.btnDialIn = true
      this.btnStatus.btnUnhold = true
      this.btnStatus.btnBusy = true
      this.btnStatus.btnIdle = true
      this.btnStatus.btnMeeting = true
    },
    // 获取坐席列表,存到seatsList
    handleExtensionList(object) {
      if (object.success) {
        const test = object.resultText
        const index = test.indexOf(',')
        if (index > 0) {
          const messageList = test.split(',')
          console.log(messageList.length)
          for (let i = 0; i < messageList.length; i++) {
            const tList = messageList[i].split('|')
            const seat = {
              exten: tList[0],
              name: tList[1],
              loginId: tList[2],
              sector: tList[3],
              group: tList[4],
              state: tList[5],
              busy: tList[6]
            }
            this.seatsList.push(seat)
          }
        }
      }
    },
    // 显示错误信息
    showError(message) {
      this.$message.error(message)
    },
    // 判断坐席状态
    judgeStatus() {
      if (this.signStatus) {
        return true
      } else {
        this.$message.warning('请先签入呼叫平台')
        return false
      }
    },
    // 判断通话状态
    judgeCallStatus() {
      if (this.isInCall) {
        return true
      } else {
        this.$message.warning('未在通话中')
        return false
      }
    },
    judgeInsideNumber(number) {
      if (number.length > 4) {
        return false
      } else {
        return true
      }
    },
    // 向后台写记录
    sendRecord(type, recordName) {
      seatReport(type, recordName)
    },
    click(method) {
      console.log('click this')
      switch (method) {
        case 'hangup':
          this.hangup()
          break
        case 'hold':
          this.hold()
          break
        case 'unhold':
          this.unhold()
          break
        case 'transfer':
          this.transferDial()
          break
        case 'meeting':
          this.meeting()
          break
        default:
          break
      }
    }
  }
}
</script>

<style rel="stylesheet/scss" lang="scss" scoped>
  .ivr-div{
    position: fixed;
    top:0px;
    left:240px;
    padding-top: 10px;
    .el-button--default{
      width:50px;
      display: inline-block;
      line-height: 1;
      white-space: nowrap;
      cursor: pointer;
      background-color:transparent;
      border-style:none;
      color: #ffffff;
      -webkit-appearance: none;
      text-align: center;
      -webkit-box-sizing: border-box;
      box-sizing: border-box;
      outline: none;
      margin: 0;
      -webkit-transition: .1s;
      transition: .1s;
      font-weight: 500;
      -moz-user-select: none;
      -webkit-user-select: none;
      -ms-user-select: none;
      padding: 3px 3px;
      font-size: 12px;
      .ivr-icon{
        width: 100%;
        text-align: center;
        display: block;
        font-size: 25px;
        margin-bottom: 2px;
      }
    }
    .el-button.is-disabled, .el-button.is-disabled:hover, .el-button.is-disabled:focus{
      color: #e1e1e1;
      .ivr-icon{
        color: #e1e1e1 !important;
      }
    }
    .state-show{
      display: inline-block;
      width:200px;
      margin-left: 20px;
      border:1px solid #409EFF;
      color:#ffffff;
      font-size:12px;
      line-height: 17px;
      padding:5px 5px;
      height: 45px;
      box-sizing: border-box;
      transform: translate(0, -8px);
    }
    .highlight{
      font-size:13px;
      font-weight: 600;
      color:#00ffff;
      padding-left: 5px;
    }
  }
</style>
<style rel="stylesheet/scss" lang="scss" >
  .addcase-dialog{
    .el-dialog__body{
      display: flex;
      justify-content: space-between;
    }
  }
  .dot {
    font-family: simsun;
  }
  :root .dot {
    display: inline-block;
    width: 1.5em;
    vertical-align: bottom;
    overflow: hidden;
  }
  @-webkit-keyframes dot {
    0% { width: 0; margin-right: 1.5em; }
    33% { width: .5em; margin-right: 1em; }
    66% { width: 1em; margin-right: .5em; }
    100% { width: 1.5em; margin-right: 0;}
  }
  .dot {
    -webkit-animation: dot 3s infinite step-start;
  }

  @keyframes dot {
    0% { width: 0; margin-right: 1.5em; }
    33% { width: .5em; margin-right: 1em; }
    66% { width: 1em; margin-right: .5em; }
    100% { width: 1.5em; margin-right: 0;}
  }
  .dot {
    animation: dot 3s infinite step-start;
  }
</style>