<!-- 呼叫页面 --> <template> <div id="rong-template-call"> <div class="rong-box rong-call-box"> <div class="rong-info"> <p> 当前拨号用户: <span>{{ params.userId }} - {{ $route.params.userId }}</span> </p> <!-- <p>群组 id: <span>{{params.groupId}}- {{$route.params.groupId}}</span></p> --> <!-- <el-button @click="test()">群聊测试</el-button> --> </div> <div class="rong-type-box" style="display: none"> <!-- <div class="rong-type-box" v-if="callStep === CallStep.READY_TO_CALL"> --> <label for="">通话类型: </label> <select name="" id="" v-model="callType"> <option value="1">单人</option> <option value="3">群组</option> </select> </div> <div class="rong-video-box"> <div class="rong-video-list"> <div class="rong-video-min" v-for="user in minUserList" v-video="user" :talktype="user.talkType" ></div> </div> <div v-if="maxUser" class="rong-video-max" v-video="maxUser" :talktype="maxUser.talkType" ></div> </div> <div class="rong-call-btns"> <template v-if="callStep === CallStep.READY_TO_CALL"> <button class="rong-call-video" title="点击进行视频通话" @click="inputCall(false)" ></button> <button class="rong-call-audio" title="点击进行音频通话" @click="inputCall(true)" ></button> </template> <template v-else-if="callStep === CallStep.CALLING"> <button class="rong-call-hungup" title="点击挂断" @click="hungup" ></button> <button v-if="callType == 3" class="rong-call-invite" title="点击邀请" @click="invite" ></button> <button class="rong-call-mute" title="点击开启/关闭音频" :closed="isMuted" @click="mute" ></button> <button class="rong-call-video" closed title="点击开启/关闭视频" :closed="callInfo.mediaType !== 2" @click="setVideo" ></button> </template> <template v-else-if="callStep === CallStep.INVITED_TO_ANSWER"> <button class="rong-call-accept" @click="accept"></button> <button class="rong-call-hungup" @click="reject"></button> </template> </div> </div> <!-- 群聊 --> <el-dialog title="群聊用户" :visible.sync="dialogVisible" width="30%"> <el-checkbox :indeterminate="isIndeterminate" v-model="checkAll" @change="handleCheckAllChange" >全选当前用户 </el-checkbox> <div style="margin: 15px 0"></div> <el-checkbox-group v-model="checkedCities" @change="handleCheckedCitiesChange" > <el-checkbox v-for="userId in cities" :label="userId" :key="userId">{{ userId }}</el-checkbox> </el-checkbox-group> <span slot="footer" class="dialog-footer"> <el-button @click="dialogVisible = false">取 消</el-button> <el-button type="primary" @click="sureMemer()">确 定</el-button> </span> </el-dialog> </div> </template> <script> import { getPeoplesMembers } from "@/services"; var RongIMLib = window.RongIMLib, RongCallLib = window.RongCallLib, win = window, RongCall = window.RongCall, setting = RongCall.setting, utils = RongCall.utils; var dialog = RongCall.dialog, ConversationType = RongIMLib.CONVERSATION_TYPE, MediaType = { MEDIA_AUDIO: 1, MEDIA_VEDIO: 2, 1: "MEDIA_AUDIO", 2: "MEDIA_VEDIO", }; var isAutoClose = false; // 通话阶段 var CallStep = { READY_TO_CALL: 1, // 准备拨打(最初状态) INVITED_TO_ANSWER: 2, // 被邀请接听, 其他人拨打, 己方选择接听或拒绝 CALLING: 3, // 拨打中 }; var CallName = {}; CallName[MediaType.MEDIA_AUDIO] = "语音"; CallName[MediaType.MEDIA_VEDIO] = "视频"; // 将消息转化为调用 CallLib 需要参数 function messageToCallInfo(message) { return { conversationType: message.conversationType, targetId: message.targetId, mediaType: message.content.mediaType, }; } // 获取挂断原因 function getHungupReason(reason, message) { var reasonPrompt; var senderUserId = message.senderUserId; switch (reason) { case 8: reasonPrompt = "其他设备已处理"; break; case 11: reasonPrompt = `${senderUserId} 已取消`; break; case 12: reasonPrompt = `${senderUserId} 已拒绝`; break; case 13: reasonPrompt = `${senderUserId} 已挂断`; break; case 14: reasonPrompt = `${senderUserId} 忙碌中`; break; case 15: reasonPrompt = `${senderUserId} 未接听`; break; default: reasonPrompt = "未知原因挂断"; } return reasonPrompt; } // 己方挂断后的提示 function getSummaryText(status, message) { var senderUserId = message.senderUserId; var text; switch (status) { case 1: text = "己方已取消"; break; case 2: text = "己方已拒绝"; break; case 3: text = "己方挂断"; break; case 4: text = `收到 ${senderUserId} 的音视频邀请, 但己方忙碌中, 不处理`; break; case 5: text = "己方未接听"; break; default: text = "未知原因"; } return text; } function mediaTypeToTalkType(mediaType) { return mediaType === MediaType.MEDIA_AUDIO ? 0 : 1; } var commandEvents = { // 监听其他人邀请自己 InviteMessage: function (message, context) { var conversationType = message.conversationType === ConversationType.PRIVATE ? "单聊" : "群聊"; context.$message( `${message.senderUserId} 邀请您进行${ CallName[message.content.mediaType] }通话(${conversationType})` ); context.callInfo = messageToCallInfo(message); context.callStep = CallStep.INVITED_TO_ANSWER; }, MemberModifyMessage: function (message, context) { if (message.content.inviteUserIds.indexOf(context.selfUserId) !== -1) { var conversationType = message.conversationType === ConversationType.PRIVATE ? "单聊" : "群聊"; context.$message( `${message.senderUserId} 邀请您进行${ CallName[message.content.mediaType] }通话(${conversationType})` ); context.callInfo = messageToCallInfo(message); context.callStep = CallStep.INVITED_TO_ANSWER; } }, HungupMessage: function (message, context) { var reason = getHungupReason(message.content.reason, message); context.$message(reason); var params = { conversationType: message.conversationType, targetId: message.targetId, }; isAutoClose = true; if (message.content.reason) { RongCallLib.hungup(params, function (error) { // 置为最初的准备拨打状态 context.callStep = CallStep.READY_TO_CALL; }); } }, MediaModifyMessage: function (message, context) { var senderUserId = message.senderUserId, mediaType = message.content.mediaType; context.userList.forEach(function (user) { if (user.userId === senderUserId) { user.talkType = mediaTypeToTalkType(mediaType); } }); }, SummaryMessage: function (message, context) { // 每次拨通或者挂断都会触发此方法 var status = message.content.status; if (!isAutoClose) { var promptText = getSummaryText(status, message); context.$message(promptText); if (status === 5) { // 自己未接听, 回到初始状态 context.callStep = CallStep.READY_TO_CALL; } } if (status === 15) { context.callStep = CallStep.READY_TO_CALL; } }, }; var videoChangedEvents = { added: function (detail, context) { context.userList.push(detail); }, removed: function (detail, context) { context.userList = utils.removeArray(detail, context.userList, "userId"); }, leave: function (detail, context) { context.userList = []; }, }; async function getMembers(currentUserList, selfUserId) { let members = await getPeoplesMembers(); console.log("members", members); members = members.filter(function (user) { var currentUserIds = currentUserList.map(function (user) { return user.id || user.userId; }); return user.id !== selfUserId && currentUserIds.indexOf(user.id) === -1; }); console.log("members", members); return win.Promise.resolve(members); } function call(callParams) { var context = this; RongCallLib.call(callParams, function (error) { // 置为通话中状态 context.callStep = error ? context.callStep : CallStep.CALLING; }); context.callInfo = callParams; } function accept() { var context = this; var callInfo = context.callInfo; // callInfo 在监听到 InviteMessage 时赋值, 格式见 messageToCallInfo 方法 RongCallLib.accept(callInfo, function (error) { // 置为通话中状态 context.callStep = error ? context.callStep : CallStep.CALLING; }); } function invite() { var context = this; var callInfo = context.callInfo, inviteParams = { conversationType: ConversationType.GROUP, targetId: callInfo.targetId, inviteUserIds: [], mediaType: callInfo.mediaType, }; getMembers(context.userList, context.selfUserId) .then(function (members) { dialog.selectUser({ userList: members, confirmed: function (selectedUserList) { var selectedIds = selectedUserList.map(function (user) { return user.id; }); inviteParams.inviteUserIds = selectedIds; RongCallLib.invite(inviteParams); }, }); }) .catch(function () { context.$message("获取群组成员失败"); }); } function reject() { var context = this; var callInfo = context.callInfo; // callInfo 在监听到 InviteMessage 时赋值, 格式见 messageToCallInfo 方法 RongCallLib.reject(callInfo, function (error) { // 置为最初的准备拨打状态 context.callStep = error ? context.callStep : CallStep.READY_TO_CALL; }); } function hungup() { var context = this; var callInfo = context.callInfo; RongCallLib.hungup(callInfo, function (error) { // 置为最初的准备拨打状态 context.callStep = error ? context.callStep : CallStep.READY_TO_CALL; }); } function mute() { var isMuted = this.isMuted; var event = isMuted ? RongCallLib.unmute : RongCallLib.mute; event(); this.isMuted = !isMuted; } function setVideo() { var callInfo = this.callInfo, mediaType = callInfo.mediaType; var event = mediaType === MediaType.MEDIA_AUDIO ? RongCallLib.audioToVideo : RongCallLib.videoToAudio; event(); this.callInfo.mediaType = mediaType === MediaType.MEDIA_AUDIO ? MediaType.MEDIA_VEDIO : MediaType.MEDIA_AUDIO; } export default { name: "rong-template-call", data() { return { userList: [], selectUserId: "", selectUserShow: true, userId: this.$route.params.userId ? this.$route.params.userId : "1", params: "", callStep: CallStep.READY_TO_CALL, callType: ConversationType.PRIVATE, // 通话类型, 默认为单聊 /** * 通话信息. 给 callInfo 赋值的地方有: * 1. 发送 call 成功后, 存储当前通话信息 * 2. 接收到 InviteMessage 后, 存储通话信息 */ callInfo: {}, targetId: "", isMuted: false, dialogVisible: false, callBool: false, checkAll: false, checkedCities: [], cities: [], isIndeterminate: true, }; }, directives: { video: function (el, binding) { var user = binding.value; var video = user.data; el.appendChild(video); video.play(); }, }, computed: { CallStep: function () { return CallStep; }, // 大窗口用户 maxUser: function () { var context = this; var maxUser; context.userList.forEach(function (user) { if (user.userId === context.selfUserId) { maxUser = user; } }); if (maxUser) { maxUser.talkType = mediaTypeToTalkType(context.callInfo.mediaType); } return maxUser; }, // 小窗口用户列表 minUserList: function () { var context = this, maxUser = context.maxUser || {}; return context.userList.filter(function (user) { return user.userId !== maxUser.userId; }); }, selfUserId: function () { return this.params.userId; }, }, methods: { async test() { let { members } = await getPeoplesMembers(); let newMemers = []; members = members.map((items) => { if (items.id !== this.targetId) { newMemers.push(items.id || items.userId); } }); this.cities = newMemers; this.dialogVisible = true; }, handleCheckAllChange(val) { this.checkedCities = val ? this.cities : []; this.isIndeterminate = false; }, handleCheckedCitiesChange(value) { let checkedCount = value.length; this.checkAll = checkedCount === this.cities.length; this.isIndeterminate = checkedCount > 0 && checkedCount < this.cities.length; }, sureMemer() { // 群聊 var context = this; var params = context.params, groupId = params.groupId; var callParams = { conversationType: ConversationType.GROUP, targetId: groupId, inviteUserIds: [], mediaType: mediaType, }; getMembers(context.userList, context.selfUserId) .then(function (members) { dialog.selectUser({ userList: members, confirmed: function (selectedUserList) { var selectedIds = selectedUserList.map(function (user) { return user.id; }); callParams.inviteUserIds = selectedIds; context.call(callParams); }, }); }) .catch(function () { context.$message("获取群组成员失败"); }); }, inputCall(bool) { // 输入用户id var targetId = this.$route.query.phone || ""; if (targetId !== "") { // url中有参数phone,则进行直接呼叫用户 this.callBool = bool; this.startCall(); return false; } this.$prompt("请输入用户id", "温馨提示", { confirmButtonText: "确定", cancelButtonText: "取消", inputErrorMessage: "用户id", }).then(({ value }) => { // 用户id this.targetId = value; this.callBool = bool; this.startCall(); }); }, /** * @param {boolean} isOnlyAudio 是否仅以音频发起 */ startCall: function (isOnlyAudio) { var mediaType = isOnlyAudio ? MediaType.MEDIA_AUDIO : MediaType.MEDIA_VEDIO; if (this.callType == ConversationType.GROUP) { this.startGroupCall(mediaType); } else { // 视频 this.startPrivateCall(mediaType); } }, startPrivateCall: function (mediaType) { var targetId = this.$route.query.phone || ""; if (targetId === "") { targetId = this.targetId; } targetId && this.call({ conversationType: ConversationType.PRIVATE, targetId: targetId, inviteUserIds: [targetId], mediaType: mediaType, }); }, startGroupCall: function (mediaType) { var context = this; var params = context.$route.params, groupId = params.groupId; var callParams = { conversationType: ConversationType.GROUP, targetId: groupId, inviteUserIds: [], mediaType: mediaType, }; getMembers(context.userList, context.selfUserId) .then(function (members) { dialog.selectUser({ userList: members, confirmed: function (selectedUserList) { var selectedIds = selectedUserList.map(function (user) { return user.id; }); callParams.inviteUserIds = selectedIds; context.call(callParams); }, }); }) .catch(function () { context.$message("获取群组成员失败"); }); }, async login(callBack) { var userId = this.userId; let _this = this; var loginDetail; let result = await this.$axios.login({ userId, }); if (result.code !== 200) { _this.$message("登录失败, 请检查 CallLib Demo Server 是否启动"); } else { setting.token = result.token; this.targetId = result.userId; loginDetail = result; let RongCall = window.RongCall; RongCall.initIM(setting); _this.params = loginDetail; callBack ? callBack(this) : null; } }, initLib() { var context = this; // 初始化 RongCallLib = RongCall.initCallLib(context.selfUserId); // 注册命令(消息)监听 RongCallLib.commandWatch(function (message) { var event = commandEvents[message.messageType]; event && event(message, context); console.log("received message", message); }); // 注册音视频节点监听 RongCallLib.videoWatch(function (result) { var event = videoChangedEvents[result.type]; event && event(result, context); console.log("video changed", result); }); let phone = this.$route.query.phone || ""; if (phone !== "") { // 默认视频通话 context.$message("正在拨通视频,请稍后..."); setTimeout(() => { this.startCall(false); }, 2000); } }, call: call, accept: accept, invite: invite, reject: reject, hungup: hungup, mute: mute, setVideo: setVideo, }, mounted() { let params = this.$route.params; // 原始代码登录 获取到的数据 console.log("原始登录:", params); params = Object.keys(params); if (params.length === 0) { // 有phone,直接进行拨打 // this.login(this.initLib); } else { // 通过login页面进入的场景 this.initLib(); } }, }; </script> <style scoped> </style>