Files
2026-01-12 17:52:15 +08:00

296 lines
8.5 KiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<view class="bottom-drawer-container" v-if="modelValue">
<view class="drawer-overlay" @tap="close"></view>
<view class="bottom-drawer" :class="{ 'drawer-open': modelValue }">
<view class="drawer-header">
<text style="color: rgba(213, 224, 242, 1); font-size: 32rpx;">选择连麦方式</text>
<text
style="font-size: 24rpx; font-weight: 400; color: rgba(124, 133, 166, 1); margin-top: 20rpx;">选择连麦方式,主播同意后接通</text>
</view>
<view class="drawer-content">
<view class="drawer-actions">
<view style="height: 2rpx; color: rgba(79, 88, 107, 0.3); width: 750rpx; background: #fff; opacity: 0.2;">
</view>
<view style="display: flex; flex-direction: row; align-items: center; padding: 30rpx;"
@click="handleSendCoGuest('video')">
<image src="/static/images/mode.png" style="width: 36rpx; height: 36rpx;"></image>
<text
style="font-size: 32rpx; font-weight: 400; color: rgba(213, 224, 242, 1); padding-left: 10rpx;">申请视频连麦</text>
</view>
<view style="height: 2rpx; color: rgba(79, 88, 107, 0.3); width: 750rpx; background: #fff; opacity: 0.2;">
</view>
<view style="display: flex; flex-direction: row; align-items: center; padding: 30rpx;"
@click="handleSendCoGuest('mic')">
<image src="/static/images/live-comic.png" style="width: 36rpx; height: 36rpx;"></image>
<text
style="font-size: 32rpx; font-weight: 400; color: rgba(213, 224, 242, 1); padding-left: 10rpx;">申请语音连麦</text>
</view>
</view>
<!-- <view class="divider-line-container">
<view class="divider-line"></view>
</view> -->
</view>
</view>
</view>
</template>
<script setup lang="ts">
import { watch, onMounted, ref, onUnmounted } from 'vue';
import { useDeviceState } from "@/uni_modules/tuikit-atomic-x/state/DeviceState";
import { useCoGuestState } from "@/uni_modules/tuikit-atomic-x/state/CoGuestState";
import { useLoginState } from "@/uni_modules/tuikit-atomic-x/state/LoginState";
const { loginUserInfo } = useLoginState()
const currentCoGuestType = ref('')
const {
// 响应式状态 - 麦克风相关
microphoneStatus, microphoneStatusReason, microphoneLastError, hasPublishAudioPermission, captureVolume,
// 响应式状态 - 摄像头相关
cameraStatus, cameraStatusReason, cameraLastError,
// 响应式状态 - 其他设备相关
currentAudioRoute, isScreenSharing, isFrontCamera, screenStatus, screenStatusReason,
// 操作方法 - 麦克风相关callback在params中
openLocalMicrophone, closeLocalMicrophone, muteLocalAudio, unmuteLocalAudio, setAudioRoute,
// 操作方法 - 摄像头相关callback在params中
openLocalCamera, closeLocalCamera, switchCamera, switchMirror, updateVideoQuality,
// 操作方法 - 屏幕共享相关callback在params中
startScreenShare, stopScreenShare,
} = useDeviceState(uni?.$liveID);
const {
// 响应式状态
invitees, applicants,
applyForSeat,
addCoGuestHostListener, removeCoGuestHostListener,
addCoGuestGuestListener, removeCoGuestGuestListener
} = useCoGuestState(uni?.$liveID);
const defaultAvatarURL = 'https://web.sdk.qcloud.com/component/TUIKit/assets/avatar_01.png';
const props = defineProps({
modelValue: {
type: Boolean,
default: false
},
liveID: {
type: String,
},
userID: {
type: String,
default: '',
},
seatIndex: {
type: Number,
default: -1,
},
});
const emit = defineEmits(['update:modelValue']);
const close = () => {
emit('update:modelValue', false);
};
onMounted(() => {
addCoGuestGuestListener(uni.$liveID, 'onGuestApplicationResponded', handleGuestApplicationResponded)
addCoGuestGuestListener(uni.$liveID, 'onGuestApplicationNoResponse', handleGuestApplicationNoResponse)
})
onUnmounted(() => {
currentCoGuestType.value = ''
removeCoGuestGuestListener(uni.$liveID, 'onGuestApplicationResponded', handleGuestApplicationResponded)
removeCoGuestGuestListener(uni.$liveID, 'onGuestApplicationNoResponse', handleGuestApplicationNoResponse)
})
const handleGuestApplicationResponded = {
callback: (event) => {
const res = JSON.parse(event)
if (res.isAccept) {
uni.$localGuestStatus = 'CONNECTED'
if (currentCoGuestType.value === 'video') {
openLocalCamera({
isFront: true,
success: () => {
console.log('openLocalCamera success.');
},
fail: (errCode, errMsg) => {
console.error(`openLocalCamera fail errCode: ${errCode}, errMsg: ${errMsg}`);
},
});
}
openLocalMicrophone({
success: () => {
console.log('openLocalMicrophone success.');
},
fail: (errCode, errMsg) => {
console.error(`openLocalMicrophone fail errCode: ${errCode}, errMsg: ${errMsg}`);
},
});
} else {
uni.$localGuestStatus = 'IDLE'
uni.showToast({
title: '上麦申请被拒绝',
icon: 'none',
duration: 2000,
});
}
}
}
const handleGuestApplicationNoResponse = {
callback: (event) => {
const res = JSON.parse(event)
if (res.reason === 'TIMEOUT') {
uni.$localGuestStatus = 'IDLE'
uni.showToast({
title: '上麦申请超时',
icon: 'none',
duration: 2000,
});
}
}
}
const handleSendCoGuest = (type : string) => {
console.log(`goGuest localStatus: ${uni.$localGuestStatus}`);
currentCoGuestType.value = type
if (uni.$localGuestStatus === 'USER_APPLYING') {
console.log(`cancel userID: ${props?.userID}`);
uni.showToast({
title: '你已提交了连麦申请 \n请勿重复申请',
icon: 'none',
duration: 2000,
position: 'center',
});
close();
return
}
if (uni.$localGuestStatus === 'IDLE') {
uni.showToast({
title: '你提交了连麦申请 \n请等待主播同意',
icon: 'none',
duration: 2000,
position: 'center',
});
applyForSeat({
liveID: props.liveID,
seatIndex: props.seatIndex, // 申请上麦传 -1, 随机分配麦位, xinlxin 反馈
timeout: 30,
});
uni.$localGuestStatus = 'USER_APPLYING'
close();
return
}
}
</script>
<style>
.bottom-drawer-container {
position: fixed;
bottom: 0;
left: 0;
right: 0;
top: 0;
z-index: 1000;
}
.drawer-overlay {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.4);
}
.bottom-drawer {
position: absolute;
bottom: 0;
left: 0;
right: 0;
background: rgba(34, 38, 46, 1);
border-top-left-radius: 32rpx;
border-top-right-radius: 32rpx;
transform: translateY(100%);
transition-property: transform;
transition-duration: 0.3s;
transition-timing-function: ease;
flex-direction: column;
height: 500rpx;
}
.drawer-open {
transform: translateY(0);
}
.drawer-header {
padding: 48rpx;
flex-direction: column;
justify-content: space-between;
align-items: center;
}
.drawer-header-title {
font-size: 32rpx;
color: rgba(255, 255, 255, 0.9);
}
.drawer-content {
flex: 1;
}
.drawer-actions {
display: flex;
flex-direction: column;
/* justify-content: space-around; */
/* justify-content: flex-start; */
}
.action-btn {
flex-direction: column;
align-items: center;
margin-right: 40rpx;
flex: 1;
height: 150rpx;
}
.action-btn-image-container {
width: 100rpx;
height: 100rpx;
background-color: rgba(43, 44, 48, 1);
margin-bottom: 12rpx;
border-radius: 25rpx;
justify-content: center;
align-items: center;
}
.action-btn-image {
width: 50rpx;
height: 50rpx;
}
.action-btn-content {
font-size: 24rpx;
color: rgba(255, 255, 255, 0.9);
}
.divider-line-container {
height: 68rpx;
justify-content: center;
position: relative;
}
.divider-line {
width: 268rpx;
height: 10rpx;
border-radius: 200rpx;
background-color: #ffffff;
position: absolute;
bottom: 16rpx;
}
.camera-mic-setting {
flex: 1;
background-color: #1f1024;
}
</style>