Ai_GirlFriend/xuniYou/pages/emCallKitPages/multiCall.nvue

411 lines
11 KiB
Plaintext
Raw Normal View History

2026-01-31 19:15:41 +08:00
<template>
<div class="multi_call_container">
<!-- 频道内邀请按钮 -->
<image
class="invite_btn"
@click="entryInviteMoreMembers"
src="/static/emCallKit/group2x.png"
></image>
<!-- 流展示容器 -->
<waterfall class="rtc_view_container" column-count="2" column-width="auto">
<cell>
<view>
<rtc-surface-view
v-if="state.engine"
class="local_view"
:uid="0"
></rtc-surface-view>
<text class="rtc_in_channel_name">{{
agoraChannelStore.emClientInfos.loginUserId
}}</text>
</view>
</cell>
<cell v-for="uid in state.remoteUids">
<view>
<rtc-surface-view
class="remote_view"
:uid="uid"
:channelId="state.channelId"
:zOrderMediaOverlay="true"
></rtc-surface-view>
<text class="rtc_in_channel_name">{{
state.inChannelMapHxId[uid] || uid
}}</text>
</view>
</cell>
</waterfall>
<!-- 页面控制 -->
<view class="rtc_control">
<view class="circleBoxView">
<text class="hint">{{ formatTime }}</text>
</view>
<view class="circleBoxView">
<view class="circleBox" @click="onSwitchLocalMicPhone">
<image
class="circleImg"
:src="
state.isMuteLocalAudioStream
? '/static/emCallKit/icon_video_quiet.png'
: '/static/emCallKit/icon_video_microphone.png'
"
></image>
<text class="hint">麦克风</text>
</view>
<view class="circleBox" @click="onSwitchSperkerPhone">
<image
class="circleImg"
:src="
state.isSwitchSperkerPhone
? '/static/emCallKit/icon_video_speaker.png'
: '/static/emCallKit/icon_video_speakerno.png'
"
></image>
<text class="hint">扬声器</text>
</view>
<view class="circleBox" @click="onSwitchLocalCameraOpened">
<image
class="circleImg"
:src="
state.isSwitchLocalCameraOpened
? '/static/emCallKit/icon_video_speaker.png'
: '/static/emCallKit/icon_video_speakerno.png'
"
></image>
<text class="hint">摄像头</text>
</view>
</view>
<view class="circleBoxView">
<view class="circleBox" @click="leaveChannel">
<image
class="circleImg"
src="/static/emCallKit/icon_video_cancel.png"
></image>
<text class="hint">挂断</text>
</view>
</view>
<image
class="switchCamera"
@click="onSwitchCamera"
src="/static/emCallKit/iconxiangjifanzhuan.png"
></image>
</view>
</div>
</template>
<script setup>
import { ref, reactive, computed } from 'vue';
import { onLoad, onUnload } from '@dcloudio/uni-app';
import { AGORA_APP_ID } from '@/components/emCallKit/config/index.js';
import { CALLSTATUS, CALL_TYPES } from '@/components/emCallKit/contants';
import RtcEngine, { RtcChannel } from '@/components/Agora-RTC-JS/index';
import {
RtcEngineContext,
LogConfig,
} from '@/components/Agora-RTC-JS/common/Classes';
import {
ClientRole,
ChannelProfile,
} from '@/components/Agora-RTC-JS/common/Enums';
import RtcSurfaceView from '@/components/Agora-RTC-JS/RtcSurfaceView';
import useAgoraChannelStore from '@/components/emCallKit/stores/channelManger';
//保持屏幕常亮
uni.setKeepScreenOn({
keepScreenOn: true,
});
//获取移动端授权权限
import permision from '@/js_sdk/wa-permission/permission';
//store
const agoraChannelStore = useAgoraChannelStore();
//channelInfos
const callKitStatus = computed(() => {
return agoraChannelStore.callKitStatus;
});
//channelName
const channelName = computed(
() => agoraChannelStore.callKitStatus.channelInfos?.channelName
);
const state = reactive({
engine: undefined,
channelId: '',
isJoined: false,
remoteUids: [],
inChannelMapHxId: {},
isSwitchCamera: true,
isSwitchSperkerPhone: true,
isMuteLocalAudioStream: false,
isSwitchLocalCameraOpened: true,
});
//开启通话计时
const inChannelTimer = ref(null);
const timeCount = ref(0);
const startInChannelTimer = () => {
inChannelTimer.value && clearInterval(inChannelTimer.value);
inChannelTimer.value = setInterval(() => {
timeCount.value++;
// console.log('%c通话计时开启中...', 'color:green', timeCount);
}, 1000);
};
//转换为可直接渲染的时间
const formatTime = computed(() => {
const m = Math.floor(timeCount.value / 60);
const s = timeCount.value % 60;
const h = Math.floor(m / 60);
const remMin = m % 60;
return `${h > 0 ? h + ':' : ''}${remMin < 10 ? '0' + remMin : remMin}:${
s < 10 ? '0' + s : s
}`;
});
//频道监听
const addListeners = () => {
state.engine.addListener('JoinChannelSuccess', (channel, uid, elapsed) => {
console.info('JoinChannelSuccess', channel, uid, elapsed);
state.isJoined = true;
});
state.engine.addListener('UserJoined', async (uid, elapsed) => {
console.info('UserJoined', uid, elapsed);
state.remoteUids = [...state.remoteUids, uid];
const { result } = await agoraChannelStore.requestInChannelMapHxId();
console.log('>>>>>>频道内环信ID获取完毕', result);
state.inChannelMapHxId = { ...result };
});
state.engine.addListener('UserOffline', (uid, reason) => {
console.info('UserOffline', uid, reason);
state.remoteUids = state.remoteUids.filter((item) => uid != item);
});
state.engine.addListener('LeaveChannel', (stats) => {
console.info('LeaveChannel', stats);
state.isJoined = false;
// state.remoteUid = '';
});
};
//初始化频道实例
const initEngine = async () => {
console.log('>>>>>>>初始化声网RTC');
try {
// state.engine = await RtcEngine.create(AGORA_APP_ID);
/**
* 该创建方式可以在本地缓存相关日志,有助于问题排查。
* 日志文件的完整路径。默认路径为:
* Android: /storage/emulated/0/Android/data/<package_name>/files/agorasdk.log
* iOS: App Sandbox/Library/caches/agorasdk.log
*/
const context = new RtcEngineContext(
AGORA_APP_ID,
undefined,
new LogConfig()
);
state.engine = await RtcEngine.createWithContext(context);
if (uni.getSystemInfoSync().platform === 'android') {
await permision.requestAndroidPermission(
'android.permission.RECORD_AUDIO'
);
await permision.requestAndroidPermission('android.permission.CAMERA');
}
await state.engine.enableVideo();
await state.engine.startPreview();
await state.engine.setChannelProfile(ChannelProfile.LiveBroadcasting);
await state.engine.setClientRole(ClientRole.Broadcaster);
//设置频道麦克风为扬声器模式
await state.engine.setDefaultAudioRoutetoSpeakerphone(true);
await joinChannel();
addListeners();
} catch (error) {
console.log('initEngine error: ', error);
}
};
initEngine();
//加入频道
const joinChannel = async () => {
try {
let { accessToken, agoraUserId } =
await agoraChannelStore.requestRtcChannelToken();
console.log(
'>>>>>>频道token请求完成',
accessToken,
agoraUserId,
channelName.value
);
(await state.engine) &&
state.engine.joinChannel(
accessToken,
channelName.value,
null,
agoraUserId
);
startInChannelTimer();
} catch (error) {
console.log('>>>>>>频道加入失败', error);
uni.showToast({ icon: 'none', title: '多人会议加入失败,请稍后重试!' });
}
};
//释放硬件设备占用
const destroyAgoraEngine = () => {
state.engine && state.engine.destroy();
console.log('>>>>>执行释放媒体设备');
};
//挂断
const leaveChannel = async () => {
if (
[CALLSTATUS.inviting, CALLSTATUS.confirmRing].includes(
agoraChannelStore.callKitStatus.localClientStatus
)
) {
(await state.engine) && state.engine.leaveChannel();
agoraChannelStore.handleCancelCall();
} else {
(await state.engine) && state.engine.leaveChannel();
//设置本地状态为闲置
agoraChannelStore.updateLocalStatus(CALLSTATUS.idle);
uni.showToast({
icon: 'none',
title: `通话结束【${formatTime.value}】`,
});
}
uni.reLaunch({
url: '../home/index',
});
destroyAgoraEngine();
};
//切换摄像头
const onSwitchCamera = () => {
state.engine &&
state.engine
.switchCamera()
.then(() => {
state.isSwitchCamera = !state.isSwitchCamera;
})
.catch((err) => {
console.warn('switchCamera', err);
});
};
//切换扬声器
const onSwitchSperkerPhone = async () => {
try {
(await state.engine) &&
state.engine.setEnableSpeakerphone(!state.isSwitchSperkerPhone);
state.isSwitchSperkerPhone = !state.isSwitchSperkerPhone;
} catch (error) {
uni.showToast({ icon: 'none', title: '扬声器切换失败!' });
}
};
//开启关闭本地麦克风采集
const onSwitchLocalMicPhone = async () => {
try {
(await state.engine) &&
state.engine.muteLocalAudioStream(!state.isMuteLocalAudioStream);
state.isMuteLocalAudioStream = !state.isMuteLocalAudioStream;
} catch (error) {
uni.showToast({ icon: 'none', title: '开关本地麦克风采集失败!' });
}
};
//开启关闭本地视频流采集
const onSwitchLocalCameraOpened = async () => {
try {
(await state.engine) &&
state.engine.enableLocalVideo(!state.isSwitchLocalCameraOpened);
state.isSwitchLocalCameraOpened = !state.isSwitchLocalCameraOpened;
} catch (error) {
uni.showToast({ icon: 'none', title: '开关本地摄像头采集失败!' });
}
};
//发起频道中邀请
const entryInviteMoreMembers = () => {
uni.navigateTo({
url: '/pages/emCallKitPages/inviteMembers',
});
};
onLoad(() => {
console.log('+++++++multiCall onLoad');
});
onUnload(() => {
state.isJoined = false;
//卸载组件清除通话计时
//清除通话计时
inChannelTimer.value && clearInterval(inChannelTimer.value);
});
</script>
<style>
.multi_call_container {
flex: 1;
background-color: #6f6b69;
padding: 150rpx 0;
}
.rtc_view_container {
flex: 1;
}
.local_view {
width: 350rpx;
height: 350rpx;
margin: 25rpx 0;
background: #000;
}
.remote_view {
width: 350rpx;
height: 350rpx;
margin: 25rpx 0;
/* margin: 5%; */
background: pink;
}
.rtc_in_channel_name {
text-align: center;
color: #fff;
}
/* 频道控制 */
.rtc_control {
width: 92%;
position: fixed;
bottom: 48rpx;
margin: 2% 4%;
align-items: center;
justify-content: center;
flex-direction: column;
/* #ifdef APP-PLUS-NVUE */
width: 680rpx;
margin: 36rpx;
/* #endif */
}
.circleBoxView {
flex-direction: row;
justify-content: flex-start;
align-items: center;
}
.circleBox {
width: 200rpx;
padding: 30rpx 0;
margin: 10rpx;
align-items: center;
flex-direction: column;
}
.circleImg {
width: 128rpx;
height: 128rpx;
}
.hint {
font-size: 24rpx;
color: #ffffff;
padding-top: 36rpx;
}
.switchCamera {
position: fixed;
bottom: 120rpx;
right: 100rpx;
width: 90rpx;
height: 90rpx;
}
.invite_btn {
position: fixed;
right: 50rpx;
top: 80rpx;
width: 75rpx;
height: 75rpx;
}
</style>