Files
uniapp-im-shop/pages/room/room.nvue
2026-02-05 17:54:43 +08:00

557 lines
12 KiB
Plaintext
Raw 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="video-call-container">
<!-- 顶部状态栏 -->
<view class="status-bar">
<view class="status-left">
<text class="status-duration">{{callDuration}}</text>
<!-- <view class="network-status">
<view class="network-dot"></view>
<text>良好</text>
</view> -->
</view>
</view>
<!-- 主视频区域 -->
<view class="main-video-area">
<!-- 对方视频 -->
<view class="remote-video"></view>
<!-- 用户信息 -->
<view class="user-info">
<view class="user-avatar">
<uni-icons type="contact" size="24" color="#fff"></uni-icons>
</view>
<view class="user-details">
<text class="user-name">{{targetUser}}</text>
<text class="call-status">通话中</text>
</view>
</view>
<!-- 本地预览 -->
<view class="local-preview" @click="switchVideo">
<view class="local-video"></view>
<view class="preview-mask">
<uni-icons type="reload" size="14" color="#fff"></uni-icons>
<text>切换</text>
</view>
</view>
</view>
<!-- 底部控制栏 -->
<view class="control-bar">
<!-- 第一行控制按钮 -->
<view class="control-row">
<view class="control-item" @click="microphone">
<view class="control-icon">
<uni-icons :type="isMicrophoneOn ? 'mic' : 'micoff'" size="24" color="#fff"></uni-icons>
</view>
<text class="control-text">{{isMicrophoneOn ? '关闭' : '开启'}}麦克风</text>
</view>
<view class="control-item" @click="closeCameraCur">
<view class="control-icon">
<uni-icons :type="isCameraOn ? 'eye' : 'eye-slash'" size="24" color="#fff"></uni-icons>
</view>
<text class="control-text">{{isCameraOn ? '关闭' : '开启'}}摄像头</text>
</view>
<view class="control-item" @click="enableSpeaker">
<view class="control-icon">
<uni-icons :type="isSpeakerOn ? 'sound' : 'sound-filled'" size="24" color="#fff"></uni-icons>
</view>
<text class="control-text">{{isSpeakerOn ? '开启' : '关闭'}}扬声器</text>
</view>
</view>
<!-- 第二行控制按钮 -->
<view class="control-row">
<view class="control-item" @click="changeMediaType">
<view class="control-icon">
<uni-icons :type="mediaType === 'video' ? 'phone' : 'videocam'" size="28"
color="#fff"></uni-icons>
</view>
<text class="control-text">{{mediaType === 'video' ? '切换语音' : '切换视频'}}</text>
</view>
<view class="hangup-btn" @click="hangup">
<view class="hangup-icon">
<uni-icons type="phone" size="28" color="#fff"></uni-icons>
</view>
<text class="hangup-text">挂断</text>
</view>
<view class="control-item" @click="switchCamera">
<view class="control-icon">
<uni-icons type="refresh" size="28" color="#fff"></uni-icons>
</view>
<text class="control-text">翻转</text>
</view>
</view>
</view>
<!-- 来电接听界面 -->
<view class="incoming-call" v-if="isIncomingCall">
<view class="caller-info">
<view class="caller-avatar">
<uni-icons type="contact-filled" size="40" color="#fff"></uni-icons>
</view>
<text class="caller-name">{{callerName}}</text>
<text class="call-type">视频通话邀请</text>
</view>
<view class="incoming-controls">
<view class="incoming-control" @click="declineCall">
<view class="call-btn decline-btn">
<uni-icons type="phone" size="28" color="#fff"></uni-icons>
</view>
<text class="btn-text">拒绝</text>
</view>
<view class="incoming-control" @click="acceptCall">
<view class="call-btn accept-btn">
<uni-icons type="phone" size="28" color="#fff"></uni-icons>
</view>
<text class="btn-text">接听</text>
</view>
</view>
</view>
</view>
</template>
<script setup>
import {
ref,
onMounted
} from 'vue'
import {
onLoad
} from '@dcloudio/uni-app'
// #ifdef APP-PLUS
import * as call from "@/nativeplugins/RongCloud-CallWrapper/lib/index";
// #endif
// 状态变量
const isConnected = ref(true)
const isMicrophoneOn = ref(true)
const isCameraOn = ref(true)
const isSpeakerOn = ref(true)
const isIncomingCall = ref(false)
const mediaType = ref('video')
const callDuration = ref('00:00')
const targetUser = ref('张伟')
const callerName = ref('李娜')
// 获取屏幕的高度
const windowWidth = ref(0)
const windowHeight = ref(0)
// 通话信息
const callform = ref({
mediaType: "video",
callType: "out",
groupId: "",
userIds: "",
targetId: "", //呼叫对象id
callWay: 0, //呼叫方式 0 单聊 1 群聊
mediaTypeCur: "audio"
})
// 初始化
onLoad(() => {
uni.getSystemInfo({
success: function(res) {
windowWidth.value = res.windowWidth;
windowHeight.value = res.windowHeight;
}
})
uni.getStorage({
key: "room-parameters",
success: (res) => {
callform.value.mediaType = res.data.mediaType;
callform.value.callType = res.data.callType ? res.data.callType : 'in';
callform.value.groupId = res.data.groupId ? res.data.groupId : '';
callform.value.userIds = res.data.userIds ? res.data.userIds : '';
if (callform.value.callType === 'out') {
console.log('呼出out')
callform.value.targetId = res.data.targetId;
startCall();
} else {
console.log('呼入接受')
// this.accept();
}
}
});
// 添加事件监听
// uni.$on('OnCallConnected', this.onCallConnected)
// uni.$on('OnCallDisconnected', this.onCallDisconnected)
})
function startCall(){
const type = callform.value.mediaType === 'audio' ? 0 : 1;
callform.value.mediaTypeCur = callform.value.mediaType;
if (callform.value.targetId.length > 0) {
call.enableSpeaker(true);
call.startSingleCall(callform.value.targetId, type, null);
// this.currentCallSession = call.getCurrentCallSession();
// this.users = this.currentCallSession.users ? this.currentCallSession.users : [];
} else {
call.startGroupCall(this.groupId, this.userIds, [], type, '');
// this.users = this.userIds;
// console.log(this.users);
// this.currentCallSession = call.getCurrentCallSession();
// this.users = this.currentCallSession.users ? this.currentCallSession.users : [];
}
// im.getCurrentUserId(function(result){
// _this.systemInfoSync(result.userId,_this.$refs.bigVideoView.ref,false);
// })
// if (this.currentCallSession) {
// this.systemInfoSync(this.currentCallSession.mine.userId, this.$refs.bigVideoView.ref, false);
// }
}
// 通话计时
let timer = null
const startCallTimer = () => {
let seconds = 0
timer = setInterval(() => {
seconds++
const minutes = Math.floor(seconds / 60)
const secs = seconds % 60
callDuration.value = `${minutes.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`
}, 1000)
}
// 切换麦克风
const microphone = () => {
isMicrophoneOn.value = !isMicrophoneOn.value
}
// 切换摄像头
const closeCameraCur = () => {
isCameraOn.value = !isCameraOn.value
}
// 切换扬声器
const enableSpeaker = () => {
isSpeakerOn.value = !isSpeakerOn.value
}
// 切换通话类型
const changeMediaType = () => {
mediaType.value = mediaType.value === 'video' ? 'audio' : 'video'
}
// 切换摄像头方向
const switchCamera = () => {
// 实现摄像头切换逻辑
}
// 切换视频画面
const switchVideo = () => {
// 实现画面切换逻辑
}
// 接听来电
const acceptCall = () => {
isIncomingCall.value = false
isConnected.value = true
}
// 拒绝来电
const declineCall = () => {
isIncomingCall.value = false
}
// 挂断通话
const hangup = () => {
if (isIncomingCall.value) {
declineCall()
} else {
isConnected.value = false
clearInterval(timer)
uni.showToast({
title: '通话已结束',
icon: 'none'
})
setTimeout(() => {
uni.navigateBack()
}, 1500)
}
}
</script>
<style lang="scss">
/* 原有样式保持不变仅需将px单位改为rpx */
.video-call-container {
width: 750rpx;
height: 100vh;
position: relative;
background: #000;
color: #fff;
}
.status-bar {
position: absolute;
top: 0;
left: 0;
right: 0;
height: 88rpx;
padding-top: 44rpx;
padding-left: 20rpx;
padding-right: 20rpx;
background: linear-gradient(to bottom, rgba(0, 0, 0, 0.8), rgba(0, 0, 0, 0));
display: flex;
align-items: center;
justify-content: space-between;
z-index: 20;
}
.status-left {
display: flex;
align-items: center;
gap: 12rpx;
}
.status-duration {
font-size: 18rpx;
font-weight: 600;
}
.network-status {
display: flex;
align-items: center;
gap: 4rpx;
font-size: 14rpx;
color: rgba(255, 255, 255, 0.8);
}
.network-dot {
width: 8rpx;
height: 8rpx;
background: #4cd964;
border-radius: 50%;
}
.main-video-area {
width: 100%;
height: 100%;
background: #000;
position: relative;
}
.remote-video {
width: 100%;
height: 100%;
}
.user-info {
position: absolute;
top: 100rpx;
left: 20rpx;
display: flex;
align-items: center;
gap: 12rpx;
}
.user-avatar {
width: 60rpx;
height: 60rpx;
background: linear-gradient(135deg, #667eea, #764ba2);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
}
.user-details {
display: flex;
flex-direction: column;
gap: 4rpx;
}
.user-name {
font-size: 20rpx;
font-weight: 500;
}
.call-status {
font-size: 14rpx;
color: rgba(255, 255, 255, 0.8);
}
.local-preview {
position: absolute;
top: 120rpx;
right: 20rpx;
width: 120rpx;
height: 160rpx;
border-radius: 8rpx;
overflow: hidden;
border: 2rpx solid rgba(255, 255, 255, 0.3);
}
.local-video {
width: 100%;
height: 100%;
}
.preview-mask {
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 30rpx;
background: linear-gradient(to top, rgba(0, 0, 0, 0.8), transparent);
display: flex;
align-items: center;
justify-content: center;
gap: 4rpx;
font-size: 12rpx;
color: rgba(255, 255, 255, 0.9);
}
.control-bar {
position: absolute;
bottom: 0;
left: 0;
right: 0;
// height: 220rpx;
width: 750rpx;
background: linear-gradient(to top, rgba(0, 0, 0, 0.9), rgba(0, 0, 0, 0.3));
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding-bottom: 40rpx;
z-index: 30;
}
.control-row {
display: flex;
justify-content: center;
gap: 40rpx;
width: 100%;
margin-bottom: 30rpx;
flex-direction: row;
}
.control-item {
display: flex;
flex-direction: column;
align-items: center;
gap: 8rpx;
}
.control-icon {
width: 80rpx;
height: 80rpx;
background: rgba(255, 255, 255, 0.3);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
}
.control-text {
font-size: 24rpx;
color: #fff;
}
.hangup-btn {
display: flex;
flex-direction: column;
align-items: center;
gap: 8rpx;
}
.hangup-icon {
width: 80rpx;
height: 80rpx;
background: #ff3b30;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
}
.hangup-text {
font-size: 24rpx;
color: #fff;
font-weight: 500;
}
.incoming-call {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: #1a1a1a;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
z-index: 100;
}
.caller-info {
text-align: center;
margin-bottom: 60rpx;
}
.caller-avatar {
width: 100rpx;
height: 100rpx;
background: linear-gradient(135deg, #4facfe, #00f2fe);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin: 0 auto 20rpx;
}
.caller-name {
font-size: 24rpx;
font-weight: 600;
margin-bottom: 8rpx;
}
.call-type {
font-size: 16rpx;
color: rgba(255, 255, 255, 0.7);
}
.incoming-controls {
display: flex;
gap: 60rpx;
}
.incoming-control {
display: flex;
flex-direction: column;
align-items: center;
gap: 8rpx;
}
.call-btn {
width: 70rpx;
height: 70rpx;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
}
.decline-btn {
background: #ff3b30;
}
.accept-btn {
background: #4cd964;
}
.btn-text {
font-size: 14rpx;
color: #fff;
}
</style>