赞
踩
先把封装好的地址安上(非本人封装):webrtc-webphone: 基于JsSIP开发的webrtc软电话
jssip中文文档:jssip中文开发文档(完整版) - 简书
jssip使用文档:(我没有运行过,但是他写的很清楚,反正比我好)jssip+webrtc+freeswitch实现电话网页及遇到的488状态码问题_freeswitch 488_weixin_39715323的博客-CSDN博客
正常使用由于web限制应该在https上使用,但是http也不是不可以,我放到下一篇文章了,这就说怎么使用
我这用的是webrtc+jssip
webrtc-webphone已经实现了我的需求,所以我没有使用原生jssip
特主要实现的功能有:注册,拨叫,接听,保持,恢复
我的项目中需要静音(指我不能说话,通话人可以讲话)
所以我将hold(保持)和unhold(恢复)改成了mute和unmute
下面是完整代码和方法说明:
1.init(注册citbar)config中需要使用ip地址、端口号、extNo拨号人、extPwd密码
2.handleAgentBarBtnClick(通话状态更改)
makecall(拨号)
hangup(挂断)
hold(静音)
unhold(取消静音)
3.onbeforeunload (通话中刷新对讲群组中没有退出,导致群组中有多个同一个人)
4.beforeDestroy(切换页面后没有退出群组,刷新不走这个方法)
注意:
1.不要重复拨号,状态卡住后就能同一个设备对话了bug
2.如果想切换页面还能通话就不要beforeDestroy,但是在其他页面在回来时会导致状态不一致,而且容易出现卡状态,所以我将通话操作放在index最顶层里面
- <template>
- <div class="top">
-
- <div style="padding: 0 30px;">
- <div style="display: flex;padding-top: 3px;">
-
- <!-- 设备树插件 -->
- <organizationTree ref="organizationTree"></organizationTree>
-
- <div style="width:77vw;height:80vh; margin-top: 5px;">
- <div class="photo_date" style="position:relative">
- <div style="display:flex;min-width:30%">
- <div class="text_type photo_deviceName " v-show="groupName" style="text-align: center;">
- {{groupName}}</div>
- <div class="text_type" :class="meetingStatus==2?' meetingName1':' meetingName'"
- style="width: 125px;text-align: center;">
- {{meetingStatus==2?meetingName:meetingName1}}</div>
- </div>
- <div style="position: absolute;right: 0;" v-show="createBy==userName">
-
- <div v-show="meetingStatus==2" class="btn_type photo_meeting_btn" @click="meetingAll"
- style="position:absolute; right:135px;width: 140px;height: 35px;line-height: 35px;">
- 邀请全部成员</div>
- <div class="bg_btn_type photo_meeting_btn" @click="meeting"
- style="position:absolute; right:0;width: 125px;height: 35px;line-height: 35px;">
- {{meetingStatus==1?'开始会议':'结束会议'}}</div>
-
- </div>
- </div>
-
- <div class="">
- <div class="deviceList_title">
- <div class="deviceList_title_text" style="width:15%">名称</div>
- <div class="deviceList_title_text" style="width:15%">imei</div>
- <div class="deviceList_title_text" style="width:15%">类型</div>
- <div class="deviceList_title_text" style="width:15%">状态</div>
- <div class="deviceList_title_text" style="width:15%">会议状态</div>
- <div class="deviceList_title_text" style="width:24%">操作</div>
- <!-- <div class="deviceList_title_text">操作</div> -->
- </div>
- <div v-show="deviceList" class="deviceList">
-
- <div class="deviceList_list" :class="item.id==itemId?'deviceList_list1':''"
- v-for="(item,index) in deviceList" :key="item.id" @mouseover="mouseover(item.id)"
- @mouseleave="mouseout()">
- <!-- <el-tooltip :content="item.name" placement="bottom" effect="light"> -->
- <div class="deviceList_title_text" style="width:15%">{{item.devName}}</div>
- <!-- </el-tooltip> -->
- <div class="deviceList_title_text" style="width:15%">{{item.imei}}</div>
- <div class="deviceList_title_text" style="width:15%">{{item.devTypeName}}
- </div>
- <div class="deviceList_title_text" style="width:15%">
- {{dictionary(item.devStatus,'dev_status',item.imei)}}
- </div>
- <div class="deviceList_title_text" style="width:15%">
- {{dictionary(item.memberStatus,'meeting_member_status')}}
- </div>
- <div style="width:24%;">
- <div v-if="meetingStatus==2">
- <div v-if="item.imei == createBy&&item.imei == userName">
- <div v-if="item.devStatus==3||item.devStatus==4||item.devStatus==5||item.devStatus==1">
- <div style="width:100%;justify-content: center;" class="deviceList_operate">
- <div class="deviceList_title_text1" @click="speak('request')"
- v-if="item.memberStatus!=3&&item.memberStatus!=1&&item.memberStatus!=4&&item.memberStatus!=5">
- <div>开始发言</div>
- </div>
- <div class="deviceList_title_text1" @click="speak('request')" v-if="item.memberStatus==3">
- <div>结束发言</div>
- </div>
- <div class="deviceList_title_text1" @click="outMeeting" v-if="item.memberStatus==2">离开会议
- </div>
- <div class="deviceList_title_text1" @click="inMeeting" v-if="item.memberStatus==1">进入会议
- </div>
- </div>
- </div>
- </div>
- <div v-if="item.imei != createBy">
- <div v-if="item.devStatus==3||item.devStatus==4||item.devStatus==5||item.devStatus==1">
- <div style="width:100%;justify-content: center;" class="deviceList_operate"
- v-show="createBy==userName&&isInOrOutMeeting&&deviceList[0].memberStatus==2">
- <div class="deviceList_title_text1" @click="speak('call',item.imei,'start')"
- v-if="item.memberStatus!=3&&item.memberStatus!=1&&item.memberStatus!=4&&item.memberStatus!=5">
- <div>点名发言</div>
- </div>
- <div class="deviceList_title_text1" @click="speak('call',item.imei,'end')"
- v-if="item.memberStatus==3">
- <div>结束发言</div>
- </div>
- <div class="deviceList_title_text1" @click="inOrOut('out',item.imei)"
- v-if="item.memberStatus==2">请离会议</div>
- <div class="deviceList_title_text1" @click="inOrOut('in',item.imei)"
- v-if="item.memberStatus==1">拉入会议</div>
- </div>
- </div>
- </div>
- </div>
-
- </div>
- </div>
- </div>
- </div>
- </div>
-
- </div>
-
- </div>
- </div>
-
- </template>
- <script>
- import Header from "../home/header/index";
- import Footer from "../home/footer/index";
- import webSocketClass from "@/utils/webSocket";
- import { postWarnStatus } from "@/api/AlarmRecord";
- import organizationTree from "./deviceTree/organizationTree"
- import { devicetree } from "@/api/system/deviceTree";
- import { timestampToTime } from "../../../utils/time.js"
- import { addGroup, delGroup, getGroup, listGroup, updateGroup, updateMeetingStatus, selectDeviceGroupDetailList, deviceRequestTalking, inOrOutMeeting } from "@/api/system/group";
- import { listData } from "@/api/system/dict/data";
- import Ctibar from './AgentBar/ctibar.js';
- var audio = document.getElementById('audio');
-
- var constraints = {
- audio: true,
- video: true,
- mandatory: {
- maxWidth: 640,
- maxHeight: 360
- }
- };
- URL = window.URL || window.webkitURL;
-
- var eventHandlers = {
- 'progress': function (e) {
- console.log('call is in progress');
- },
- 'failed': function (e) {
- console.log('call failed: ', e);
- },
- 'ended': function (e) {
- console.log('call ended : ', e);
- },
- 'confirmed': function (e) {
- console.log('call confirmed');
- }
- };
- export default {
- dicts: ["warn_type"],
- components: {
- Header,
- Footer,
- organizationTree
- },
- data() {
-
- return {
-
- // 以下群组
- isGroup: false,
- createBy: '',
- isInOrOutMeeting: false,
- isSpeak: false,
- itemId: '',
- meetingStatus: '',
- groupDetail: {},
- isMike: false,
- deviceList: null,
- meetingName1: "会议未开始",
- meetingName: "会议中",
- groupName: "",
- dataList: null,
- userId: JSON.parse(sessionStorage.getItem("userInfo")).userId,
- userName: JSON.parse(sessionStorage.getItem("userInfo")).userName,
- // 查询参数
- queryParams: {
- pageNum: 1,
- pageSize: 10,
- deptId: null,
- planName: null,
- status: null,
- },
- total: 0,
- deviceTreeList: [],
- title: "",
- num: 1,
- groupId: '',
- dictionaryList: [],
- websocket: null,
-
- //初始化SDK所需要的配置
- config: {
- host: '39.152.2.103',
- port: '5066',
- proto: false,
- extNo: '',
- extPwd: '20181231',
- autoRegister: true,
- debug: true,
- //stunServer: 'stun.1.google.com', 可自行修改使用stun服务器地址
- stateEventListener: this.stateEventListener
- },
- //坐席分机号
- agentNo: '',
- //客户号码
- customerNo: '',
- //拨号弹窗
- showDial: false,
- //转接弹窗
- showTransferDial: false,
- //转接号码
- transNum: '',
- numList: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '*', '0', '#',],
- agentStatus: 'DISCONNECTED',
- statusMap: {
- CONNECTED: '已连接',
- DISCONNECTED: '网络断开',
- REGISTERED: '已注册',
- UNREGISTERED: '未注册',
- REGISTER_FAILED: '注册失败',
- IN_CALL: '通话中',
- INCOMING_CALL: '来电振铃',
- OUTGOING_CALL: '外呼振铃',
- HOLD: '保持中',
- CALL_END: '通话结束'
- },
- timer: null,
- timerString: '00:00:00',
- outNum: '',
- isHold: true
- };
- },
- computed: {
- classObject() {
- const bool1 = this.alarmArr.length > 1;
- const bool2 = this.alarmArr.length === 1;
- return {
- tanchuangbox: true,
- "tanchuangbox-height-multi": bool1,
- "tanchuangbox-height-single": bool2,
- };
- },
- },
- mounted() {
-
- this.init()
-
- },
- methods: {
-
- // 以下群组
- inMeeting() {
- this.loadingFun()
- var body = {
- devImei: this.userName,
- flag: 'in',
- groupId: this.groupId,
- }
-
- inOrOutMeeting(body).then(response => {
- if (response.data.success) {
- } else {
- this.$modal.msgError(response.data.message);
- }
- this.loading.close();
- },
- error => {
- this.loading.close();
- });
-
-
-
-
- },
- outMeeting() {
- this.loadingFun()
- var body = {
- devImei: this.userName,
- flag: 'out',
- groupId: this.groupId,
- }
-
- inOrOutMeeting(body).then(response => {
-
- if (response.data.message == '已离开!') {
-
- } else {
- this.$modal.msgError(response.data.message);
- }
- this.loading.close();
- },
- error => {
- this.loading.close();
- });
-
-
-
-
- },
-
- inOrOut(flag, devImei) {
- this.loadingFun()
- var body = {
- devImei: devImei,
- flag: flag,
- groupId: this.groupId,
- }
- inOrOutMeeting(body).then(response => {
- console.log("123123123", response)
- if (response.data.success) {
- this.$modal.msgSuccess(response.data.message);
- } else {
- this.$modal.msgError(response.data.message);
- }
- this.loading.close();
- },
- error => {
- this.loading.close();
- });
- },
- loadingFun() {
- this.loading = this.$loading({
- lock: true,
- text: 'Loading',
- spinner: 'el-icon-loading',
- background: 'rgba(0, 0, 0, 0.7)'
- });
- },
- speak(type, devImei, flag) {
- this.loadingFun()
- var flagg = ''
- if (type == 'request') {
- flagg = !this.isSpeak ? 'start' : 'end'
- devImei = this.userName
- } else {
- flagg = flag
- }
- var body = {
- devImei: devImei,
- flag: flagg,
- groupId: this.groupId,
- type: type,
- }
- deviceRequestTalking(body).then(response => {
- if (response.data.success) {
- if (type == 'request') {
-
- }
- this.$modal.msgSuccess(response.data.message);
- //恢复发言
- } else {
- this.$modal.msgError(response.data.message);
- this.hold()
- }
- this.loading.close();
- },
- error => {
- this.loading.close();
- });
- },
- // 1、进入元素
- mouseover(index) {
- this.itemId = index
- },
- // 4、离开元素
- mouseout() {
- this.itemId = ''
- },
- ws() {
- console.log('{"groupId":' + this.groupId + '}')
- this.websocket.webSocketSendMsg('{"groupId":' + this.groupId + '}')
-
- },
- devStatus(devStatus) {
- for (const i in this.deviceList) {
- if (this.deviceList[i].devStatus == devStatus) {
- this.deviceList[i].devStatus = devStatus
- }
- }
- },
- dictionary(e, type, imei) {
- for (const i in this.dictionaryList) {
- if (this.createBy == imei && e == 4) {
- return '在线'
- }
- if (this.dictionaryList[i].dictValue == e && this.dictionaryList[i].dictType == type) {
- return this.dictionaryList[i].dictLabel
- }
- }
- },
- handleAdd() {
- // this.reset();
- // this.open = true;
- this.title = "添加群组";
- },
-
- tableRowClassName({ row, rowIndex }) {
-
- return 'photo';
- },
- rowClass({ row, rowIndex }) {
-
-
- return 'text-align: center;background-color: #1A1D30;color: #fff'
-
- },
- meetingAll() {
- var meetingStatusAll = 2
- var params = {
- groupId: this.groupId,
- meetingStatus: meetingStatusAll
- }
- updateMeetingStatus(params).then(response => {
- if (response.code == 200) {
-
- this.$modal.msgSuccess(response.msg);
- } else {
- this.$modal.msgError(response.msg);
-
- }
- });
- },
-
- meeting() {
- this.loadingFun()
- if (this.deviceList != null) {
- console.log('meetingStatusmeetingStatus', this.meetingStatus)
- var params = {
- groupId: this.groupId,
- meetingStatus: this.meetingStatus == 1 ? 2 : 1
- }
- updateMeetingStatus(params).then(response => {
- setTimeout(() => {
- if (response.code == 200) {
- if (this.meetingStatus == 1) {
- if (sessionStorage.getItem('groupId') == this.groupId) {
- this.handleAgentBarBtnClick('hangup')
- sessionStorage.setItem('groupId', '')
- }
- this.isInOrOutMeeting = false
- this.isHold = true
-
- this.loading.close()
- }
- }
- }, 5000);
-
- });
- }
- },
- //孙组件向父组件传递数据
- wsMeetingStatus(isInterface) {
- console.log('isMeeting状态:', isInterface)
- this.meetingStatus = isInterface
- },
- groupDeviceItemClick(item) {
- // this.handleAgentBarBtnClick('hangup')
- this.isSpeak = false
- this.deviceList = []
- this.groupId = item.id
- this.meetingStatus = item.meetingStatus
- this.groupName = item.groupName
- this.createBy = item.createBy
- this.item = item
- var params = { groupInfoId: item.id, pageNum: 0, pageSize: 0, }
- selectDeviceGroupDetailList(params).then(response => {
- this.deviceList = response.rows
- this.total = this.deviceList.length
- this.ws()
- if (this.deviceList[0].memberStatus == 3 &&
- this.deviceList[0].imei == this.userName) {
- this.isSpeak = true;
- }
- if (
- this.deviceList[0].memberStatus == 2 &&
- this.deviceList[0].imei == this.userName
- ) {
- if (sessionStorage.getItem("groupId") == this.groupId) {
- this.isInOrOutMeeting = true
- return
- }
- if (sessionStorage.getItem("groupId")) {
- } else {
- this.call()
- }
- }
- });
-
- },
-
-
-
-
- onClickDialOutside(event) {
- console.log(event)
- this.showDial = false
- },
- onClickTransDialOutside(event) {
- this.showTransferDial = false;
- },
- handleAgentBarBtnClick(name) {
- console.log(name + '当前')
- if (name === 'login') {
- this.login();
- } else if (name === 'logout') {
- this.logout();
- } else if (name === 'answer') {
- this.answer();
- } else if (name === 'hangup') {
- this.hangup();
- } else if (name === 'makecall') {
- this.makeCall('9*' + this.groupId)
- } else if (name === 'hold') {
- this.hold();
- } else if (name === 'unhold') {
- this.unhold();
- } else if (name === 'transfer') {
- this.transfer(this.transNum)
- }
- },
-
- login() {
- this.init()
- Ctibar.register()
- },
- logout() {
- Ctibar.unregister()
- },
- makeCall(phone) {
- if (phone === "" || phone === undefined) {
- console.error('无效的号码,请重新输入!');
- return
- }
- Ctibar.makecall(phone);
-
- },
- hold() {
- Ctibar.hold();
-
- },
- unhold() {
- Ctibar.unhold();
- },
- answer() {
- Ctibar.answer();
- },
- hangup() {
- Ctibar.hangup();
- },
- transfer(phone) {
- console.info("触发转接", phone)
- Ctibar.transfer(phone);
- },
- //外呼拨号盘
- handleDialBtnClick(val) {
- this.outNum += val;
- this.$refs.outNumInput.focus();
- },
- //转接拨号盘
- handleTransDialBtnClick(val) {
- this.transNum += val;
- this.$refs.transNumInput.focus();
- },
-
- //参数为时间差秒数,返回这两个时间差并格式化
- computeTimeDiff(diff) {
- diff = Math.round(diff / 1000);
- let hour = Math.floor(diff / 3600).toString().padStart(2, '0');
- let min = Math.floor((diff - hour * 3600) / 60).toString().padStart(2, '0');
- let sec = (diff % 60).toString().padStart(2, '0');
- return hour + ':' + min + ':' + sec;
- },
- //重置时间
- restoreTime(origin) {
- clearInterval(this.timer);
- this.timerString = '00:00:00';
- this.timer = setInterval(() => {
- this.timerString = this.computeTimeDiff(new Date().getTime() - origin);
- }, 1000);
- },
- //状态变更回调
- stateEventListener(event, data) {
- //debug使用
- console.log('当前event为: ' + event + ', 当前data为: ' + JSON.stringify(data))
-
- this.agentStatus = event
- let origin = new Date().getTime();
- switch (event) {
- case "CONNECTED":
- this.agentNo = data.localAgent
- this.restoreTime(origin);
- break;
-
- case "DISCONNECTED":
- this.restoreTime(origin);
- break;
-
- case "UNREGISTERED":
- this.restoreTime(origin);
- break;
-
- case "OUTGOING_CALL":
- this.customerNo = data.otherLegNumber;
- this.restoreTime(origin);
- break;
-
- case "INCOMING_CALL":
- this.customerNo = data.otherLegNumber;
- //播放来电振铃音
- this.playRingMedia();
- this.restoreTime(origin);
- break;
-
- case "IN_CALL":
- this.stopPlayRingMedia();
- this.restoreTime(origin);
-
- // this.timer = setInterval(() => {
- // }, 1000);
- console.log('当前是否hold', this.isHold)
- if (this.isHold) {
- setTimeout(() => {
- this.handleAgentBarBtnClick('hold')
- this.isHold = false
- // 方法区
- }, 500);
- }
-
- break;
-
- case "CALL_END":
- this.stopPlayRingMedia();
- //挂机铃声
- this.playHangupMedia()
- this.restoreTime(origin);
- break;
-
- default:
-
- }
- },
-
- //播放挂机铃声
- playHangupMedia() {
- const _this = this;
- var hangupAudio = document.getElementById("hangupMediaAudioId")
- if (!hangupAudio) {
- hangupAudio = document.createElement('audio');
- hangupAudio.id = 'hangupMediaAudioId';
- hangupAudio.hidden = true;
- hangupAudio.src = 'wav/hangup.wav'
- document.body.appendChild(hangupAudio);
- }
- hangupAudio.play();
- },
- //播放来电振铃
- playRingMedia() {
- const _this = this;
- _this.stopPlayRingMedia();
-
- var ringAudio = document.getElementById("ringMediaAudioId")
- if (!ringAudio) {
- ringAudio = document.createElement('audio');
- ringAudio.id = 'ringMediaAudioId';
- ringAudio.hidden = true;
- ringAudio.src = 'wav/ring.wav';
- ringAudio.loop = 'loop';
- document.body.appendChild(ringAudio);
- }
- ringAudio.play();
- },
- //停止播放来电振铃
- stopPlayRingMedia() {
- const _this = this;
- var ringAudio = document.getElementById("ringMediaAudioId");
- if (ringAudio) {
- document.body.removeChild(ringAudio);
- }
- },
- //初始化方法
- init() {
- this.config.extNo = this.userName
- if (sessionStorage.getItem("freeSwitchWs") != null) {
- this.config.host = sessionStorage.getItem("freeSwitchWs").split(":")[0]
- this.config.port = sessionStorage.getItem("freeSwitchWs").split(":")[1]
- }
- Ctibar.initSDK(this.config)
-
-
- let url = `/ws/` + this.userId + '/group/device/push';
- this.websocket = new webSocketClass(url)
- this.websocket.getWebSocketMsg(evt => {
- // 客户端接收服务端返回的数据
- var data = JSON.parse(evt.data);
- console.log("websocket返回的数据123:", data);
- switch (data.flag) {
- case "group"://会议状态
- this.$refs.organizationTree.setGroupList(data, this.groupId)
- console.log("websocket返回的数据123:", data);
- break
- case "memberStatus"://成员状态
- for (const i in this.deviceList) {
- console.log('123123132', data.member);
- if (this.deviceList[i].imei == data.member) {
- this.deviceList[i].devStatus = data.memberStatus;
- }
- }
- break
- case "memberMeetingStatus"://成员会议状态
- for (const i in this.deviceList) {
- if (this.deviceList[i].imei == data.member) {
- this.deviceList[i].memberStatus = data.memberMeetingStatus;
- }
- }
- if (this.groupId == data.groupId && this.userName == data.member && this.createBy == data.member) {
- if (data.memberMeetingStatus == 3) {
- this.isSpeak = true;
- this.unhold()
- console.log('数据this.isInOrOutMeeting1111', this.isSpeak)
- } else {
- this.isSpeak = false;
- this.hold()
- }
- }
-
- if (data.memberMeetingStatus == 4) {
- this.loading.close()
- }
- if (!this.isInOrOutMeeting && this.groupId == data.groupId && this.userName == data.member && this.createBy == data.member && data.memberMeetingStatus == 2) {
- this.hold()
- if (sessionStorage.getItem("groupId") &&
- sessionStorage.getItem("groupId") !== this.groupId) {
- } else {
- console.log('this.isInOrOutMeeting1111', data.memberMeetingStatus)
- this.call()
- }
- }
- if (this.createBy == data.member && data.memberMeetingStatus == 1 && this.userName == data.member) {
- this.handleAgentBarBtnClick('hangup')
- sessionStorage.setItem("groupId", '')
- this.isInOrOutMeeting = false
- this.isSpeak = false
- this.isHold = true
- }
- break
-
- }
- })
- },
- setGroup(group) {
- this.isGroup = group
-
-
- },
- call() {
- this.handleAgentBarBtnClick('makecall')
- sessionStorage.setItem("groupId", this.groupId)
- this.isInOrOutMeeting = true
- if (this.deviceList[0].imei != this.createBy) {
- // this.groupDeviceItemClick(this.item)
- }
- var that = this
- window.onbeforeunload = (e) => {
- console.log('this.isInOrOutMeeting', that.isInOrOutMeeting)
- if (that.isInOrOutMeeting) {
- that.handleAgentBarBtnClick('hangup')
- sessionStorage.setItem("groupId", '')
- that.websocket.closeSocket()
- window.onbeforeunload = null
- }
- }
- this.loading.close()
- this.isInOrOutMeeting = true
- this.isHold = true
- }
- },
- beforeDestroy() { //进行监听销毁
- console.log('1231232131232132131,', this.isInOrOutMeeting)
- if (this.isInOrOutMeeting) {
- this.handleAgentBarBtnClick('hangup')
- sessionStorage.setItem("groupId", '')
- this.websocket.closeSocket()
- window.onbeforeunload = null
- }
- },
-
-
-
- };
- </script>
赞
踩
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。