import TRTCCalling from './TRTCCalling/TRTCCalling.js'; const TAG_NAME = 'TUICalling'; // 组件旨在跨终端维护一个通话状态管理机,以事件发布机制驱动上层进行管理,并通过API调用进行状态变更。 // 组件设计思路将UI和状态管理分离。您可以通过修改`component`文件夹下的文件,适配您的业务场景, // 在UI展示上,您可以通过属性的方式,将上层的用户头像,名称等数据传入组件内部,`static`下的icon和默认头像图片, // 只是为了展示基础的效果,您需要根据业务场景进行修改。 // eslint-disable-next-line no-undef Component({ properties: { config: { type: Object, value: { sdkAppID: 0, userID: '', userSig: '', type: 1, tim: null, }, }, backgroundMute: { type: Boolean, value: false, }, }, observers: { }, data: { callStatus: 'idle', // idle、calling、connection isSponsor: false, pusher: {}, // TRTC 本地流 playerList: [], // TRTC 远端流 remoteUsers: [], // 远程用户资料 screen: 'pusher', // 视屏通话中,显示大屏幕的流(只限1v1聊天 soundMode: 'speaker', // 声音模式 听筒/扬声器 }, methods: { initCall() { // 收起键盘 wx.hideKeyboard(); }, // 新的邀请回调事件 handleNewInvitationReceived(event) { this.initCall(); console.log(`${TAG_NAME}, handleNewInvitationReceived, event${JSON.stringify(event)}`); this.data.config.type = event.data.inviteData.callType; this.getUserProfile([event.data.sponsor]); this.setData({ config: this.data.config, callStatus: 'calling', isSponsor: false, }); }, // 用户接听 handleUserAccept(event) { console.log(`${TAG_NAME}, handleUserAccept, event${JSON.stringify(event)}`); this.setData({ callStatus: 'connection', }); }, // 远端用户进入通话 handleUserEnter(res) { this.setData({ playerList: res.playerList, }); }, // 远端用户离开通话 handleUserLeave(res) { this.setData({ playerList: res.data.playerList, }); }, // 用户数据更新 handleUserUpdate(res) { console.log(`${TAG_NAME}, handleUserUpdate, res`); console.log(res); this.handleNetStatus(res.data); this.setData({ pusher: res.data.pusher, playerList: res.data.playerList, }); }, // 判断网络状态 handleNetStatus(data) { if (data.pusher.netQualityLevel > 4) { wx.showToast({ icon: 'none', title: '您当前网络不佳', }); } data.playerList.map((item) => { if (item.netStatus.netQualityLevel > 4) { const name = data.playerList.length > 1 ? item.nick : '对方'; wx.showToast({ icon: 'none', title: `${name}当前网络不佳`, }); } return item; }); }, // 用户拒绝 handleInviteeReject(event) { console.log(`${TAG_NAME}, handleInviteeReject, event${JSON.stringify(event)}`); wx.showToast({ title: `${this.handleCallingUser([event.data.invitee])}已拒绝`, }); }, // 用户不在线 handleNoResponse(event) { console.log(`${TAG_NAME}, handleNoResponse, event${JSON.stringify(event)}`); wx.showToast({ title: `${this.handleCallingUser(event.data.timeoutUserList)}不在线`, }); }, // 用户忙线 handleLineBusy(event) { console.log(`${TAG_NAME}, handleLineBusy, event${JSON.stringify(event)}`); wx.showToast({ title: `${this.handleCallingUser([event.data.invitee])}忙线中`, }); }, // 用户取消 handleCallingCancel(event) { console.log(`${TAG_NAME}, handleCallingCancel, event${JSON.stringify(event)}`); wx.showToast({ title: `${this.handleCallingUser([event.data.invitee])}取消通话`, }); }, // 通话超时未应答 handleCallingTimeout(event) { console.log(`${TAG_NAME}, handleCallingTimeout, event${JSON.stringify(event)}`); wx.showToast({ title: `${this.handleCallingUser(event.data.timeoutUserList)}超时无应答`, }); }, handleCallingUser(userIDList) { const remoteUsers = [...this.data.remoteUsers]; const userProfile = remoteUsers.filter(item => userIDList.some(userItem => `${userItem}` === item.userID)); this.setData({ remoteUsers: remoteUsers.filter(item => userIDList.some(userItem => userItem !== item.userID)), }); console.log(`${TAG_NAME}, handleCallingUser, userProfile`, userProfile); let nick = ''; for (let i = 0; i < userProfile.length; i++) { nick += `${userProfile[i].nick}、`; } return nick.slice(0, -1); }, // 通话结束 handleCallingEnd(event) { console.log(`${TAG_NAME}, handleCallingEnd`); console.log(event); this.reset(); if (event.data.message) { this.triggerEvent('sendMessage', { message: event.data.message, }); } wx.showToast({ title: '通话结束', duration: 800, }); }, // SDK Ready 回调 handleSDKReady() { // 呼叫需在sdk ready之后 }, // 被踢下线 handleKickedOut() { wx.showToast({ title: '您已被踢下线', }); }, // 切换通话模式 handleCallMode(event) { this.data.config.type = event.data.type; this.setData({ config: this.data.config, }); this.triggerEvent('sendMessage', { message: event.data.message, }); }, // 增加 tsignaling 事件监听 _addTSignalingEvent() { // 被邀请通话 wx.$TRTCCalling.on(wx.$TRTCCalling.EVENT.INVITED, this.handleNewInvitationReceived, this); // 用户接听 wx.$TRTCCalling.on(wx.$TRTCCalling.EVENT.USER_ACCEPT, this.handleUserAccept, this); // 用户进入通话 wx.$TRTCCalling.on(wx.$TRTCCalling.EVENT.USER_ENTER, this.handleUserEnter, this); // 用户离开通话 wx.$TRTCCalling.on(wx.$TRTCCalling.EVENT.USER_LEAVE, this.handleUserLeave, this); // 用户离开通话 wx.$TRTCCalling.on(wx.$TRTCCalling.EVENT.USER_UPDATE, this.handleUserUpdate, this); // 用户拒绝通话 wx.$TRTCCalling.on(wx.$TRTCCalling.EVENT.REJECT, this.handleInviteeReject, this); // 用户无响应 wx.$TRTCCalling.on(wx.$TRTCCalling.EVENT.NO_RESP, this.handleNoResponse, this); // 用户忙线 wx.$TRTCCalling.on(wx.$TRTCCalling.EVENT.LINE_BUSY, this.handleLineBusy, this); // 通话被取消 wx.$TRTCCalling.on(wx.$TRTCCalling.EVENT.CALLING_CANCEL, this.handleCallingCancel, this); // 通话超时未应答 wx.$TRTCCalling.on(wx.$TRTCCalling.EVENT.CALLING_TIMEOUT, this.handleCallingTimeout, this); // 通话结束 wx.$TRTCCalling.on(wx.$TRTCCalling.EVENT.CALL_END, this.handleCallingEnd, this); // SDK Ready 回调 wx.$TRTCCalling.on(wx.$TRTCCalling.EVENT.SDK_READY, this.handleSDKReady, this); // 被踢下线 wx.$TRTCCalling.on(wx.$TRTCCalling.EVENT.KICKED_OUT, this.handleKickedOut, this); // 切换通话模式 wx.$TRTCCalling.on(wx.$TRTCCalling.EVENT.CALL_MODE, this.handleCallMode, this); }, // 取消 tsignaling 事件监听 _removeTSignalingEvent() { // 被邀请通话 wx.$TRTCCalling.off(wx.$TRTCCalling.EVENT.INVITED, this.handleNewInvitationReceived); // 用户接听 wx.$TRTCCalling.off(wx.$TRTCCalling.EVENT.USER_ACCEPT, this.handleUserAccept); // 用户进入通话 wx.$TRTCCalling.off(wx.$TRTCCalling.EVENT.USER_ENTER, this.handleUserEnter); // 用户离开通话 wx.$TRTCCalling.off(wx.$TRTCCalling.EVENT.USER_LEAVE, this.handleUserLeave); // 用户离开通话 wx.$TRTCCalling.off(wx.$TRTCCalling.EVENT.USER_UPDATE, this.handleUserUpdate); // 用户拒绝通话 wx.$TRTCCalling.off(wx.$TRTCCalling.EVENT.REJECT, this.handleInviteeReject); // 用户无响应 wx.$TRTCCalling.off(wx.$TRTCCalling.EVENT.NO_RESP, this.handleNoResponse); // 用户忙线 wx.$TRTCCalling.off(wx.$TRTCCalling.EVENT.LINE_BUSY, this.handleLineBusy); // 通话被取消 wx.$TRTCCalling.off(wx.$TRTCCalling.EVENT.CALLING_CANCEL, this.handleCallingCancel); // 通话超时未应答 wx.$TRTCCalling.off(wx.$TRTCCalling.EVENT.CALLING_TIMEOUT, this.handleCallingTimeout); // 通话结束 wx.$TRTCCalling.off(wx.$TRTCCalling.EVENT.CALL_END, this.handleCallingEnd); // SDK Ready 回调 wx.$TRTCCalling.off(wx.$TRTCCalling.EVENT.SDK_READY, this.handleSDKReady); // 被踢下线 wx.$TRTCCalling.off(wx.$TRTCCalling.EVENT.KICKED_OUT, this.handleKickedOut); // 切换通话模式 wx.$TRTCCalling.off(wx.$TRTCCalling.EVENT.CALL_MODE, this.handleCallMode); }, /** * C2C邀请通话,被邀请方会收到的回调 * 如果当前处于通话中,可以调用该函数以邀请第三方进入通话 * * @param userID 被邀请方 * @param type 0-为之, 1-语音通话,2-视频通话 */ async call(params) { this.initCall(); wx.$TRTCCalling.call({ userID: params.userID, type: params.type }).then((res) => { this.data.config.type = params.type; this.getUserProfile([params.userID]); this.setData({ pusher: res.pusher, config: this.data.config, callStatus: 'calling', isSponsor: true, }); this.triggerEvent('sendMessage', { message: res.data.message, }); }); }, /** * IM群组邀请通话,被邀请方会收到的回调 * 如果当前处于通话中,可以继续调用该函数继续邀请他人进入通话,同时正在通话的用户会收到的回调 * * @param userIDList 邀请列表 * @param type 1-语音通话,2-视频通话 * @param groupID IM群组ID */ async groupCall(params) { this.initCall(); wx.$TRTCCalling.groupCall({ userIDList: params.userIDList, type: params.type, groupID: params.groupID }).then((res) => { this.data.config.type = params.type; this.getUserProfile(params.userIDList); this.setData({ pusher: res.pusher, config: this.data.config, callStatus: 'calling', isSponsor: true, }); }); }, /** * 当您作为被邀请方收到 {@link TRTCCallingDelegate#onInvited } 的回调时,可以调用该函数接听来电 */ async accept() { wx.$TRTCCalling.accept().then((res) => { this.setData({ pusher: res.pusher, callStatus: 'connection', }); this.triggerEvent('sendMessage', { message: res.message, }); }); }, /** * 当您作为被邀请方收到的回调时,可以调用该函数拒绝来电 */ async reject() { console.log(`${TAG_NAME}, reject`); wx.$TRTCCalling.reject().then((res) => { this.triggerEvent('sendMessage', { message: res.data.message, }); }); this.reset(); }, // xml层,是否开启扬声器 toggleSoundMode() { this.setData({ soundMode: wx.$TRTCCalling._toggleSoundMode(), }); }, // xml层,挂断 _hangUp() { console.log(`${TAG_NAME}, hangup`); wx.$TRTCCalling.hangup(); this.reset(); }, // 切换大小屏 (仅支持1v1聊天) toggleViewSize(event) { this.setData({ screen: wx.$TRTCCalling._toggleViewSize(event), }); }, // 数据重置 reset() { this.setData({ callStatus: 'idle', isSponsor: false, pusher: {}, // TRTC 本地流 playerList: [], // TRTC 远端流 }); }, // 呼叫中的事件处理 handleCallingEvent(data) { const { name } = data.detail; switch (name) { case 'accept': this.accept(); break; case 'hangup': this._hangUp(); break; case 'reject': this.reject(); break; case 'toggleSwitchCamera': wx.$TRTCCalling.switchCamera(); break; case 'switchAudioCall': wx.$TRTCCalling.switchAudioCall().then((res) => { this.data.config.type = wx.$TRTCCalling.CALL_TYPE.AUDIO; this.setData({ config: this.data.config, }); this.triggerEvent('sendMessage', { message: res.data.message, }); }); break; default: break; } }, // 通话中的事件处理 handleConnectedEvent(data) { console.log(`${TAG_NAME}, handleVideoEvent--`, data); const { name, event } = data.detail; switch (name) { case 'toggleViewSize': this.toggleViewSize(event); break; case 'pusherNetStatus': wx.$TRTCCalling._pusherNetStatus(event); break; case 'playNetStatus': wx.$TRTCCalling._playNetStatus(event); break; case 'pusherStateChangeHandler': wx.$TRTCCalling._pusherStateChangeHandler(event); break; case 'pusherAudioVolumeNotify': wx.$TRTCCalling._pusherAudioVolumeNotify(event); break; case 'playerStateChange': wx.$TRTCCalling._playerStateChange(event); break; case 'playerAudioVolumeNotify': wx.$TRTCCalling._playerAudioVolumeNotify(event); break; case 'pusherAudioHandler': wx.$TRTCCalling._pusherAudioHandler(event); break; case 'hangup': this._hangUp(); break; case 'toggleSoundMode': this.toggleSoundMode(); break; case 'pusherVideoHandler': wx.$TRTCCalling._pusherVideoHandler(event); break; case 'toggleSwitchCamera': wx.$TRTCCalling.switchCamera(event); break; case 'switchAudioCall': wx.$TRTCCalling.switchAudioCall().then((res) => { this.data.config.type = 1; this.setData({ config: this.data.config, }); this.triggerEvent('sendMessage', { message: res.data.message, }); }); break; default: break; } }, // 获取用户资料 async getUserProfile(userList) { const imResponse = await this.getTim().getUserProfile({ userIDList: userList }); this.setData({ remoteUsers: imResponse.data, }); }, // 获取 tim 实例 getTim() { return wx.$TRTCCalling.getTim(); }, // 初始化TRTCCalling async init() { try { const res = await wx.$TRTCCalling.login({ userID: this.data.config.userID, userSig: this.data.config.userSig, }); this._addTSignalingEvent(); return res; } catch (error) { throw new Error('TRTCCalling login failure', error); } }, // 销毁 TRTCCalling destroyed() { this._removeTSignalingEvent(); if (this.data.config.tim) { wx.$TRTCCalling.destroyed(); } else { wx.$TRTCCalling.logout(); } }, }, /** * 生命周期方法 */ lifetimes: { created() { }, attached() { }, ready() { if (!wx.$TRTCCalling) { wx.$TRTCCalling = new TRTCCalling({ sdkAppID: this.data.config.sdkAppID, tim: this.data.config.tim, }); } wx.$TRTCCalling.initData(); this.reset(); }, detached() { this.reset(); }, error() { }, }, pageLifetimes: { show() { }, hide() { }, resize() { }, }, });