<template> <div id="RongYun"> <div class="rong-container"> <div id="rongUser" class="rong-user" v-show="false"> <span>用户 ID:</span> <span id="rongUserId"></span> </div> <div class="rong-im" v-show="false" id="rongIM"> <p>请先进行 IM 连接</p> <!--appkey--> <div class="im-item"> <label>App Key</label> <input type="text" id="appkey" :value="AppKey" placeholder="请输入 App Key" /> </div> <!--token--> <div class="im-item"> <label>Token</label> <input type="text" id="token" :value="tokens" placeholder="请输入 Token" /> </div> <!--navi--> <div class="im-item"> <label>Navi</label> <input type="text" id="navi" placeholder="请输入 Navi 地址" /> <p>非必填;私有云环境必填</p> </div> <!--mediaServer--> <div class="im-item"> <label>MediaServer</label> <input type="text" id="mediaServer" placeholder="请输入 MediaServer 地址" /> <p>非必填;音视频服务地址</p> </div> <!--连接--> <div class="im-item"> <button id="DengLuBTN" ref="DengLuBTN" @click="connectIM()"> 连接 </button> </div> </div> <div class="rong-call" id="rongCall" style="display: none"> <!--呼叫选项--> <div id="callParam" class="call-param"> <!--选择通话类型--> <div class="param-item" v-show="false"> <label>通话类型</label> <select name="" id="callType" @change="callTypeChange()"> <option value="1">单聊</option> <option value="3">群聊</option> </select> <p>必选</p> </div> <!--this.mediaType--> <div class="param-item" v-show="false"> <label>媒体类型</label> <select name="" id="callMediaType" @change="callMediaTypeChange()"> <option value="1">音频</option> <option value="2">音视频</option> </select> <p>必选</p> </div> <!--targetId--> <div id="paramPrivate" v-show="false" class="param-item"> <label>对方 ID</label> <input id="targetId" type="text" placeholder="对方 userId" v-model="targetIdvalue" /> <p> 必填;对方的 userId,可通过<a target="_blank" href="https://developer.rongcloud.cn/" >[开发者后台] -> [服务管理] -> [API 调用] -> [用户服务] -> [获取 token]</a >获取,且登录成功 </p> </div> <!--targetId--> <div id="paramGroupId" class="param-item" style="display: none"> <label>群组 ID</label> <input id="groupId" type="text" value="q1" placeholder="群组 ID" /> <p> 必填;可通过 <a target="_blank" href="https://developer.rongcloud.cn/" >[开发者后台] -> [服务管理] -> [API 调用] -> [群组服务] -> [加入群组]</a > 加入群组后获取 </p> </div> <!--userIds 只有群显示--> <div id="paramInvitedIds" class="param-item" style="display: none"> <label>被邀请者 ID</label> <input id="userIds" type="text" placeholder="多个 userId 用英文半角逗号分开" :value="UserList" /> <p> 必填;需加入群后,方可收到邀请。多个 userId 用英文半角逗号分开 </p> </div> </div> <!--通话视图展示--> <div id="videoView" class="video-view"></div> <!--通话操作按钮--> <div class="opt-btn" id="btn-div"> <!-- <button id="callBtn" class="btn-call" @click="call()">呼叫</button> --> <div id="callBtn" class="btn-call" @click="call()"></div> <!-- <button id="acceptBtn" class="btn-accept" @click="accept()"> 接听 --> <div id="acceptBtn" class="btn-accept" @click="accept()"></div> <div id="hungupBtn" class="btn-hungup" @click="hungup()"></div> </div> </div> </div> <div class="toast-wrap"> <span class="toast-msg"></span> </div> </div> </template> <script> import { mapGetters } from "vuex"; import { userGetToken } from "@/api/paishuiSys/patrol/patrolMange/wayManage"; import { getUserInfo } from "@/api/system/user"; export default { name: "RongYun", props: { Getphone: { type: String, default: () => "", }, getmediaType: { type: Number, default: () => 2, }, }, data() { return { targetIdvalue: "13407245473", userinfo: {}, names: "姓名", numbs: "XXX", AppKey: "25wehl3u2en6w", tokens: "", DengLuBTN: null, value: "", UserList: "11,22", imClient: "", callSession: "", callType: RongIMLib.ConversationType.PRIVATE, mediaType: RCCall.RCCallMediaType.AUDIO, rtcClient: undefined, callClient: undefined, RCDom: { get: (id) => { return document.getElementById(id); }, show: (id) => { var rongIMDom = document.getElementById(id); rongIMDom.setAttribute("style", "display:block;"); }, showBlock: (id) => { var rongIMDom = document.getElementById(id); rongIMDom.setAttribute("style", "display:block;"); }, hide: (id) => { var rongIMDom = document.getElementById(id); rongIMDom.setAttribute("style", "display:none;"); }, }, RCToast: (msg) => { setTimeout(function () { document .getElementsByClassName("toast-wrap")[0] .getElementsByClassName("toast-msg")[0].innerHTML = msg; var toastTag = document.getElementsByClassName("toast-wrap")[0]; toastTag.className = toastTag.className.replace("toastAnimate", ""); setTimeout(function () { toastTag.className = toastTag.className + " toastAnimate"; }, 10); }, 10); }, RCCallView: { connectedIM: () => { // this.RCDom.show("rongUser"); }, readyToCall: () => { this.RCDom.hide("rongIM"); this.RCDom.show("rongCall"); }, outgoing: () => { this.RCDom.hide("callParam"); this.RCDom.hide("callBtn"); this.RCDom.show("hungupBtn"); }, incomming: () => { this.RCDom.hide("callParam"); this.RCDom.hide("callBtn"); this.RCDom.show("acceptBtn"); this.RCDom.show("hungupBtn"); }, inTheCall: () => { this.RCDom.hide("acceptBtn"); this.RCDom.hide("callParam"); this.RCDom.show("hungupBtn"); }, end: () => { this.RCDom.show("callParam"); this.RCDom.show("callBtn"); this.RCDom.hide("acceptBtn"); this.RCDom.hide("hungupBtn"); }, }, }; }, computed: {}, methods: { connectIM() { const appkey = this.RCDom.get("appkey").value; const token = this.RCDom.get("token").value; const navi = this.RCDom.get("navi").value; if (!appkey) { this.RCToast("请输入 App Key"); return; } if (!token) { this.RCToast("请输入 Token"); return; } // IM 客户端初始化 this.imClient = RongIMLib.init({ appkey, navigators: navi ? [navi] : undefined, logLevel: 1, }); // 初始化 RTC CallLib this.initRTC(); this.initCall(); this.imClient.watch({ // 监听 IM 连接状态变化 status(evt) { console.log("connection status change:", evt.status); }, }); // this.RCToast("正在链接 IM ... ☕️"); this.imClient .connect({ token }) .then((user) => { this.RCCallView.connectedIM(); this.RCCallView.readyToCall(); this.RCDom.get("rongUserId").innerText = user.id; // this.RCToast(`用户 ${user.id} IM 链接成功 ✌🏻`); this.callTypeChange(); this.callMediaTypeChange(); setTimeout(() => { this.call() }, 0); }) .catch((error) => { console.log(error); this.RCToast("IM 链接失败,请检查网络后再试"); }); }, /** * RTC 初始化 * 在 IM 初始化后进行初始化 (具体位置:im.js) */ initRTC() { const mediaServer = this.RCDom.get("mediaServer").value; this.rtcClient = this.imClient.install(RCRTC.installer, { mediaServer: mediaServer || undefined, timeout: 30 * 1000, logLevel: window.RCEngine.LogLevel.DEBUG, }); console.log("this.rtcClient", this.rtcClient); }, /** * CallLib 初始化 * 在 IM 初始化后进行初始化 (具体位置:im.js) */ initCall() { this.callClient = this.imClient.install(window.RCCall.installer, { rtcClient: this.rtcClient, onSession: (session) => { this.callSession = session; this.mediaType = session.getMediaType(); this.registerCallSessionEvent(this.callSession); this.RCToast(`收到 ${session.getCallerId()} 的通话邀请`); // this.RCCallView.incomming(); }, onSessionClose: (session, summary) => { this.RCToast("通话已结束"); this.RCCallView.end(); this.removeVideoEl(); }, }); console.log("this.callClient", this.callClient); }, /** * 通话类型监听 */ callTypeChange() { const callTypeDom = this.RCDom.get("callType"); this.callType = Number(callTypeDom.value); if (this.callType === RongIMLib.ConversationType.GROUP) { this.RCDom.showBlock("paramGroupId"); this.RCDom.showBlock("paramInvitedIds"); this.RCDom.hide("paramPrivate"); } else { this.RCDom.hide("paramGroupId"); this.RCDom.hide("paramInvitedIds"); // this.RCDom.showBlock("paramPrivate"); } // // 默认多人通话 // this.RCDom.showBlock("paramGroupId"); // this.RCDom.showBlock("paramInvitedIds"); // this.RCDom.hide("paramPrivate"); }, /** * 媒体类型监听 */ callMediaTypeChange() { const mediaTypeDom = this.RCDom.get("callMediaType"); this.mediaType = Number(mediaTypeDom.value); // this.mediaType = 2; }, /** * CallSession 事件 */ getCallSessionEvent() { return { onRinging: (sender) => { // 当远端用户已开始响铃, 该函数有 2 个参数: sender 是发送者,session 是通话实例。这时用户可以在业务层做响铃的 UI 展示。 this.RCToast(`收到 ${sender.userId} 振铃`); console.log(`收到 ${sender.userId} 振铃`); // message.info(`收到 ${sender.userId} 振铃`); }, onAccept: (sender) => { // 当远端用户已同意接听, 该函数有 2 个参数: sender 是发送者,session 是通话实例。这时用户可以把 UI 的‘响铃’变成‘通话中’。 this.RCToast(`${sender.userId} 已接听`); }, onHungup: (sender) => { // 当有远端用户挂断, 该函数有 3 个参数: sender 是发送者,reason 是挂断原因,session 是通话实例。这时用户可以在 UI 层提示‘xxx已挂断’。 this.RCToast(`${sender.userId} 已挂断`); // 群组中移除相应节点 const videoViewDom = this.RCDom.get("videoView"); const videoDom = this.RCDom.get(`video-${sender.userId}`); videoDom && videoViewDom.removeChild(videoDom); }, onTrackReady: (track) => { // 当本端资源或远端资源已获取, 该函数有 2 个参数:track 是本端资源或远端资源, session 是通话实例。这时用户可以用拿到的 track 播放音频、视频。 this.appendVideoEl(track); if (!track.isLocalTrack()) { this.RCToast("通话已建立"); this.RCCallView.inTheCall(); } }, onMemberModify: (sender, invitedUsers) => { // 群组通话中有其他人被邀请加入, 该函数有 3 个参数: sender 是发送者,invitedUsers 是被邀请的用户列表, session 是通话实例。这时用户可以在 UI 层提示‘xxx加入通话’。 }, onMediaModify: (sender) => { // 通话类型改变时触发, 该函数有3个参数: sender 是发送者,mediaType 通话类型, session 是通话实例。这时用户可以在 UI 层提示‘已降级成音频通话’。 }, onAudioMuteChange: (muteUser) => { // 对方静音后触发, 该函数有2个参数: muteUser 是已静音的用户, session 是通话实例。这时用户可以在 UI 层提示‘xxx已静音’。 }, onVideoMuteChange: (muteUser) => { // 对方禁用视频后触发, 该函数有2个参数: muteUser 是已禁用视频的用户, session 是通话实例。这时用户可以在 UI 层提示‘xxx已禁用视频’。 }, }; }, /** * callSession 事件注册 */ registerCallSessionEvent(session) { const events = this.getCallSessionEvent(); session.registerSessionListener(events); }, /** * callSession 呼叫 */ call() { this.mediaType = this.getmediaType; console.log("this.getmediaType", this.getmediaType); const events = this.getCallSessionEvent(); const isPrivateCall = this.callType === RongIMLib.ConversationType.PRIVATE; const params = { targetId: this.RCDom.get(`${isPrivateCall ? "targetId" : "groupId"}`) .value, mediaType: this.mediaType, listener: events, }; if (isPrivateCall) { if (!this.RCDom.get("targetId").value) { this.RCToast("请输入对方 ID"); return; } this.privateCall(params); } else { if (!this.RCDom.get("groupId").value) { this.RCToast("请输入群组 ID"); return; } if (!this.RCDom.get("userIds").value) { this.RCToast("请输入被邀请者 ID"); return; } this.groupCall(params); } }, /** * 单呼 */ privateCall(params) { this.callClient.call(params).then(({ code, session }) => { console.log(code, session); if (code === RCCall.RCCallErrorCode.SUCCESS) { this.registerCallSessionEvent(session); this.callSession = session; this.RCCallView.outgoing(); } else { this.RCToast(`呼叫失败,错误原因:${code}`); } }); }, /** * 群呼 */ groupCall(params) { params.userIds = (this.RCDom.get("userIds").value || []).split(","); this.callClient.callInGroup(params).then(({ code, session }) => { if (code === RCCall.RCCallErrorCode.SUCCESS) { this.registerCallSessionEvent(session); this.callSession = session; this.RCCallView.outgoing(); } else { const reason = code === RCCall.RCCallErrorCode.NOT_IN_GROUP ? "当前用户未加入群组" : code; this.RCToast(`呼叫失败,错误原因:${reason}`); this.removeVideoEl(); } }); }, /** * 接听当前 callSession */ accept() { this.callSession.accept().then(({ code }) => { if (code === RCCall.RCCallErrorCode.SUCCESS) { this.RCToast("接听成功"); } else { this.RCToast(`接听失败,错误原因:${code}`); } }); }, /** * 挂断当前 callSession */ hungup() { if (this.callSession) { this.callSession.hungup().then(({ code }) => { if (code === RCCall.RCCallErrorCode.SUCCESS) { this.RCToast("挂断成功"); this.bus.$emit("closeFX"); } else { this.RCToast(`挂断失败,错误原因:${code}`); } }); } }, /** * video 视图渲染 */ appendVideoEl(track) { console.log(track); const container = this.RCDom.get("videoView"); if (track.isAudioTrack()) { const uid = track.getUserId(); const node = document.createElement("div"); node.setAttribute("id", `video-${uid}`); const videoTpl = `<span class="video-user-id">ID: ${uid}</span> <span class="video-media-type">${this.mediaType === 1 ? "音频" : ""}</span> <video id="${uid}"></video>`; // const videoTpl = `<span class="video-user-id">${uid}</span> // <span class="video-media-type">${this.mediaType === 1 ? "音频" : ""}</span> // <video id="${uid}"></video>`; node.innerHTML = videoTpl; node.classList = "video-item"; const rongCall = document.getElementById("rongCall"); const btndiv = document.getElementById("btn-div"); container.appendChild(node); // rongCall.insertBefore(node, btndiv); track.play(); } else { const videoEl = this.RCDom.get(track.getUserId()); track.play(videoEl); } }, /** * 通话结束后,清除所有 video 标签 */ removeVideoEl() { this.RCDom.get("videoView").innerHTML = ""; }, // GetuserInfo 获取用户信息 GetuserInfo() { getUserInfo().then((res) => { console.log(res); this.userinfo = res.data.data; console.log(this.userinfo); this.numbs = res.data.data.phone; this.names = res.data.data.name; this.$nextTick(() => { this.Dengl(); }); }); }, // 默认登录,获取当前电脑用户PCUser的通话token Dengl() { userGetToken(`userId=${this.numbs}&name=${this.names}`).then((res) => { console.log(res); if (res.data.code == 200) { console.log("res.data.token", res.data.token); this.tokens = res.data.token; this.$nextTick(() => { this.connectIM(); }); } }); }, }, // computed: { // ...mapGetters([ // "userInfo", // ]), // }, watch: { Getphone: { handler(val) { console.log(val); if (val) { this.targetIdvalue = val; } }, // immediate: true }, }, mounted() { this.GetuserInfo(); }, beforeDestroy() { this.hungup(); }, }; </script> <style lang="scss" scoped> @import "./rongyun.css"; #RongYun { width: 100%; height: 100%; } .rong-call input { color: #000 !important; } input:-internal-autofill-selected { -webkit-text-fill-color: #000 !important; } </style>