<template> <!-- 接听来电提醒 --> <el-dialog v-model="allData.showTip" id="tipModal" append-to-body :close-on-click-modal="false" class="responseDialog" :z-index="9999" > <template #header> <div class="tipsHeader">来电提示</div> </template> <div style="line-height: 80px; text-align: center; font-size: 16px; color: #fff"> 收到来自 {{ allData.fromCalluser }} 的{{ useRongyunStore.mediaType == 2 ? '视频' : '语音' }}邀请,是否接听??? </div> <template #footer> <div class="dialog-footer"> <el-button size="small" @click="hungup()">挂断</el-button> <el-button size="small" type="primary" @click="accept">确 定 </el-button> </div> </template> </el-dialog> <!-- 视图渲染弹窗 --> <el-dialog v-model="allData.showVideo" center id="ryAcceptModal" append-to-body :close-on-click-modal="false" :show-close="false" :z-index="9999" > <template #header> <div class="titleRy"> 远程会商 <img @click="hungup()" src="/src/assets/images/rongYunImg/gb_btn.png" class="rybtn" /> </div> </template> <div class="rong-container-app" id="rongContainer"> <div id="videoViewAPP" class="rong-video-box-app"></div> <div id="videoActionAPP" class="rong-action-app"> <img class="hunupBtn" id="hunup" src="/src/assets/images/rongYunImg/call-off.png" alt="" @click="hungup()" /> </div> </div> </el-dialog> <div class="voiceDilog" v-if="allData.showVoice"> <div class="voiceHeader"> <img class="vioceIcon" src="/src/assets/images/rongYunImg/voice.png" alt="" /> 请开启声音权限 </div> <el-button class="submit" type="primary" @click="allData.showVoice = false">确定</el-button> </div> </template> <script setup> import { ref, reactive, toRefs, onMounted, nextTick } from 'vue'; import { getToken } from '@/utils/auth'; import { listrcloudUnitUser, getUserStatus, queryToken, hangUp } from '@/views/RongyunCommunication/rongyunjs/rongyunApi.js'; import { getInfo } from '@/api/login'; import rongyunStore from '@/store/modules/rongyunStore'; const useRongyunStore = rongyunStore(); import bus from '@/utils/util'; import * as RongIMLib from '@rongcloud/imlib-next'; // 导入 RTCLib、CallLib import { installer as rtcInstaller, RCRTCClient, RCTrack, RCFrameRate, RCResolution } from '@rongcloud/plugin-rtc'; import useAppStore from '@/store/modules/app'; const appStore = useAppStore(); import { installer as callInstaller, RCCallClient, RCCallSession, RCCallErrorCode, RCCallLanguage, RCCallEndReason, RCCallMediaType, RCCallSessionState, } from '@rongcloud/plugin-call'; const { proxy } = getCurrentInstance(); const audio = new Audio(); const successUrl = 'https://192.168.100.18:10000/image/newfiber-standard-region-project/ring.mp3'; const allData = reactive({ showVoice: true, // 实例化一个音频播放器对象 tokenRy: '', appkey: 'pkfcgjjue28x8', navi: 'https://jingkai.wh-nf.cn:11444', phone: '', userName: '', showTip: false, fromCalluser: null, showVideo: false, styleRY: { width: '900px', height: '600px', }, conversationType: null, //接收类型 remoteUser: [], //房间人员列表 idList: [], //视频id-dom列表 timer: null, acceptType: '', }); const GetpromoterThridUserId = ref(''); const { showVoice, tokenRy, appkey, navi, phone, userName, showTip, fromCalluser, showVideo, styleRY, conversationType, remoteUser, idList, timer, } = toRefs(allData); onMounted(() => { bus.off('showRy'); bus.on('showRy', e => { console.log('打开融云弹框'); if (getToken()) { GetuserInfo(); } }); bus.off('rystatus'); bus.on('rystatus', e => { console.log('拨打调用', e); if (getToken()) { reconnect(); } }); console.log('getToken()', getToken()); if (getToken()) { GetuserInfo(); } }); // 播放音频 const PlayAudio = () => { let music1 = new Audio(); //建立一个music1对象 music1 = 'https://jingkai.wh-nf.cn:8900/resources/videos/ring.mp3'; //通过require引入音频 this.$refs.audio.src = music1; //此处的audio为代码ref="audio"中的audio this.$refs.audio.play(); //play()为播放函数 }; // 重连融云 const reconnect = () => { allData.tokenRy && connectIM(); useRongyunStore.SET_online(false); proxy.$modal.msgWarning('视频会商已离线,重新连接中!'); }; const getStaus = (time = 0) => { clearInterval(allData.timer); allData.timer = setInterval(async () => { useRongyunStore.SET_online(false); let params = { registerThridUserId: allData.phone + '_web', }; let res = await getUserStatus(params); console.log('轮询,', res); let flag = null; if (res.code == 200) { flag = JSON.parse(res.data); } if (flag != 1) { allData.tokenRy && connectIM(); proxy.$modal.msgWarning('视频会商已离线,重新连接中!'); } else { useRongyunStore.SET_online(true); } }, time); }; const GetuserInfo = () => { console.log('1111111111111'); getInfo().then(res => { console.log('当前登陆人信息', res); localStorage.setItem('userNo', res.data.user.userName); allData.phone = res.data.user.phonenumber; getQueryToken(); }); }; // 获取融云token const getQueryToken = async () => { let params = { registerThridUserId: allData.phone + '_web', }; let res = await queryToken(params); if (res.code == 200) { allData.tokenRy = res.data && res.data[0]?.thridToken; if (allData.tokenRy) { useRongyunStore.SET_haveRYtoken(true); getStaus(60000); allData.tokenRy && connectIM(); } } else { useRongyunStore.SET_haveRYtoken(false); useRongyunStore.SET_online(false); proxy.$modal.msgWarning('获取通讯token失败'); } }; // 获取元素 const getDom = key => { return document.querySelector(key); }; const connectEvents = Events => { /** * 正在链接的事件状态 */ RongIMLib.addEventListener(Events.CONNECTING, () => { console.log('正在链接...'); }); /** * 链接到服务器会触发这个事件 */ RongIMLib.addEventListener(Events.CONNECTED, () => { console.log('连接成功'); }); /** * 手动调用 disconnect 方法或者用户被踢下线 会触发这个事件 */ RongIMLib.addEventListener(Events.DISCONNECT, code => { console.log('连接中断,需要业务层进行重连处理 ->', code); }); /** * 链接出问题时,内部进行重新链接,会触发这个事件 */ RongIMLib.addEventListener(Events.SUSPEND, code => { console.log('链接中断,SDK 会尝试重连,业务层无需关心'); // 5.1.2 版本开始,事件回调中会引起中断的 code 状态码 console.log(`code -> ${code}`); }); }; // IM 实例 const connectIM = () => { console.log('初始化connectIM', allData.appkey); console.log('初始化connectIM22', allData.navi); console.log('初始化connectIM22', allData.tokenRy); RongIMLib.init({ appkey: allData.appkey, navigators: [allData.navi], }); initRTC(); initCall(); const token = allData.tokenRy; RongIMLib.connect(token) .then(data => { console.log('*-*-', data); // 监听融云状态 connectEvents(RongIMLib.Events); useRongyunStore.SET_online(true); proxy.$message.success(`用户 ${data.data.userId} IM 链接成功 ✌🏻`); }) .catch(error => { console.log(error); useRongyunStore.SET_online(false); proxy.$modal.msgWarning('IM 链接失败,请检查网络后再试'); }); }; // RTC const initRTC = () => { let rtcClient = RongIMLib.installPlugin(rtcInstaller); useRongyunStore.SET_RTC(rtcClient); }; // Rcall const initCall = () => { console.log('aaaa'); console.log(callInstaller, 'callInstaller'); console.log('useRongyunStore.rtcClient', useRongyunStore.rtcClient); console.log(toRaw(useRongyunStore.rtcClient), 'useRongyunStore.rtcClient'); let callClient = RongIMLib.installPlugin(callInstaller, { rtcClient: toRaw(useRongyunStore.rtcClient), /** * 被动收到邀请 (收到一个远端发起的新会话), 会产生一个新的 session 对象 (必填) */ onSession: (session, extra) => { console.log('来电拉来电拉', session, extra); allData.conversationType = session.getConversationType(); allData.remoteUser = session.getRemoteUsers(); allData.acceptType = session; useRongyunStore.SET_CALLSESSION(session); useRongyunStore.SET_MediaType(session.getMediaType()); registerCallSessionEvent(toRaw(useRongyunStore.callSession)); let callerId = session.getCallerId(); queryformUserName(callerId); GetpromoterThridUserId.value = callerId; audio.autoplay = true; audio.muted = false; audio.loop = true; audio.src = successUrl; proxy.$modal.msgWarning(`收到 ${session.getCallerId()} 的通话邀请`); allData.showTip = true; console.log('allData.showTip', allData.showTip); }, /** * 以下三条只要满足一条,就会触发onSessionClose * 1、本端用户自己主动挂断 * 2、服务端把本端用户踢出 RTC 房间 * 3、房间里小于2个人 * * @param {RCCallSession} session 被结束的 session 对象 * @param summaryInfo 结束一个 session 的后汇总信息 */ onSessionClose: (session, summaryInfo) => { console.log('通话已结束', summaryInfo); audio.pause(); proxy.$modal.msgWarning('通话已结束'); allData.showTip = false; //清除拨号界面的video视图 bus.emit('removeEle'); allData.showVideo = false; }, }); console.log(callClient, 'callClient'); useRongyunStore.SET_CALL(callClient); }; /** * CallSession 事件 */ const getCallSessionEvent = () => { return { onRinging: sender => { proxy.$modal.msgWarning(` ${sender.userId} 振铃`); }, onAccept: sender => { proxy.$modal.msgWarning(` ${sender.userId} 已接听`); }, onHungup: (sender, reason) => { proxy.$modal.msgWarning(` ${sender.userId} 已挂断`); const videoViewDom = getDom('#videoViewAPP'); const videoDom = getDom(`#video-${sender.userId}`); videoDom && videoViewDom.removeChild(videoDom); const itemdomList = document.getElementsByClassName('video-item'); if (itemdomList.length <= 1) { restore(); removeVideoEl(); allData.showVideo = false; } }, onTrackReady: track => { appendVideoEl(track); // 暂停 // 当本端资源或远端资源已获取, 该函数有 2 个参数:track 是本端资源或远端资源, session 是通话实例。这时用户可以用拿到的 track 播放音频、视频。 // 当本端资源或远端资源已获取, 该函数有 2 个参数:track 是本端资源或远端资源, session 是通话实例。这时用户可以用拿到的 track 播放音频、视频。 if (!track.isLocalTrack()) { // audio.pause(); proxy.$modal.msgWarning('通话已建立'); } }, onMemberModify: (sender, invitedUsers, session) => { console.log('触发邀请监听1111', sender, invitedUsers, session); console.log('触发邀请监听1111', allData.userList); // 群组通话中有其他人被邀请加入, 该函数有 3 个参数: sender 是发送者,invitedUsers 是被邀请的用户列表, session 是通话实例。这时用户可以在 UI 层提示‘xxx加入通话’。 }, onMediaModify: sender => { // 通话类型改变时触发, 该函数有3个参数: sender 是发送者,mediaType 通话类型, session 是通话实例。这时用户可以在 UI 层提示‘已降级成音频通话’。 }, onAudioMuteChange: muteUser => { // 对方静音后触发, 该函数有2个参数: muteUser 是已静音的用户, session 是通话实例。这时用户可以在 UI 层提示‘xxx已静音’。 proxy.$modal.msgWarning(` ${muteUser.userId} 已静音`); }, onVideoMuteChange: muteUser => { // 对方禁用视频后触发, 该函数有2个参数: muteUser 是已禁用视频的用户, session 是通话实例。这时用户可以在 UI 层提示‘xxx已禁用视频’。 proxy.$modal.msgWarning(` ${muteUser.userId} 已禁用视频`); }, }; }; /** * callSession 事件注册 */ const registerCallSessionEvent = session => { const events = getCallSessionEvent(); session.registerSessionListener(events); }; /** * 接听当前 callSession */ const accept = () => { toRaw(useRongyunStore.callSession) .accept() .then(({ code }) => { if (code === RCCallErrorCode.SUCCESS) { proxy.$modal.msgWarning('接听成功'); allData.showTip = false; allData.showVideo = true; } else { proxy.$modal.msgWarning(`接听失败,错误原因:${code}`); hungupApi(code); } }); }; /** * 挂断当前 callSession */ const hungup = () => { toRaw(useRongyunStore.callSession) .hungup() .then(({ code }) => { if (code === RCCallErrorCode.SUCCESS) { // proxy.$modal.msgWarning("挂断成功"); removeVideoEl(); restore(); allData.idList = []; allData.showTip = false; allData.showVideo = false; } else { proxy.$modal.msgWarning(`挂断失败,错误原因:${code}`); removeVideoEl(); allData.showTip = false; allData.showVideo = false; hungupApi(code); } }); }; // 接听或者挂断失败后都调 融合通讯通话 const hungupApi = code => { console.log('调取了挂断接口xxxx', GetpromoterThridUserId.value); let params = { promoterThridUserId: GetpromoterThridUserId.value, //发起人 answerThridUserId: allData.phone + '_web', //接听人 当前登录人 updateBy: localStorage.getItem('userNo'), answerType: code || null, }; console.log('调取了挂断接口xxxx', params); hangUp(params).then(res => { console.log('调取了挂断接口xxxx', res); }); }; /** * video 视图渲染 */ const appendVideoEl = async track => { const container = getDom('#videoViewAPP'); const uid = track.getUserId(); let mediaType = toRaw(useRongyunStore.mediaType); let videoTpl = null; let params = { phone: uid.split('_')[0], }; let res = await listrcloudUnitUser(params); let data = res.data[0]; if (res.code == 200) { const node = document.createElement('div'); node.setAttribute('id', `video-${uid}`); if (allData.idList.findIndex(i => i == `video-${uid}`) == -1) { allData.idList.push(`video-${uid}`); } if (mediaType == 1) { videoTpl = `<span class="video-user-id">${data.name} :${uid} </span><span class="video-media-type"></span><video id="${uid}"></video>`; } else { videoTpl = `<span class="video-user-id">${data.name} :${uid} </span><video id="${uid}"></video>`; } node.innerHTML = videoTpl; node.classList.add('video-item'); let length = allData.remoteUser.length + 1; console.log('什么东西', allData.conversationType, length); // 根据人数判断渲染不同的视图样式 if (allData.phone + '_web' == uid && length <= 2) { node.classList.add('first-child'); } if (length > 2 && length <= 4) { node.classList.add('video-item-4'); } if (length > 4 && length <= 8) { node.classList.add('video-item-8'); } if (track.isAudioTrack()) { if (mediaType == 1) { container.appendChild(node); } track.play(); } else { container.appendChild(node); let videoEl = document.getElementById(uid); track.play(videoEl); } const targetElements = document.querySelectorAll('.video-item'); console.log('targetElements', targetElements); targetElements.forEach(target => { target.classList.remove('video-item-4'); target.classList.remove('video-item-8'); if (length > 2 && length <= 4) { target.classList.add('video-item-4'); } if (length > 4 && length <= 8) { target.classList.add('video-item-8'); } }); } }; //关闭视频弹窗 const close = () => { // hungup(); }; //还原 const restore = () => { allData.styleRY = { width: '900px', height: '600px', }; const dom1 = getDom('#videoViewAPP'); if (!dom1) return false; dom1.style = ''; allData.idList.forEach((item, index) => { let videodom = getDom(`#${item}`); if (videodom) { videodom.style = null; } const titledom = getDom(`#${item} .video-user-id`); if (titledom) { titledom.style = null; } }); }; // 通过userPhone匹配用户名称 const queryformUserName = async callerId => { console.log('callerId匹配用户名称', callerId); let params = { phone: callerId.split('_')[0], }; let res = await listrcloudUnitUser(params); console.log('res获取用户', res); if (res && res.code == 200) { let name = res.data[0].name; allData.fromCalluser = `${name}`; } }; const removeVideoEl = () => { if (!getDom('#videoViewAPP')) return false; getDom('#videoViewAPP').innerHTML = ''; getDom('#videoViewAPP').style = ''; }; onBeforeUnmount(() => { RongIMLib.disconnect().then(() => console.log('断开链接成功')); }); </script> <style lang="scss"> @import '@/views/RongyunCommunication/rongyuncss/index.scss'; </style> <style lang="scss" scoped> .rybtn { cursor: pointer; position: absolute; right: 45px; top: 52px; } </style>