<template> <!-- 接听来电提醒 --> <el-dialog v-model="allData.showTip" id="tipModal" append-to-body :close-on-click-modal="false" class="responseDialog" > <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" > <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: false, // 实例化一个音频播放器对象 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(""); 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 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: 35px; top: 22px; } </style>