wx-starlock/pages/p2p/p2pPlayer.vue
2025-06-06 15:20:34 +08:00

361 lines
9.3 KiB
Vue

<template>
<view>
<iot-p2p-player-with-mjpg
v-if="deviceInfo"
id="playerRef"
:deviceInfo="deviceInfo"
:xp2pInfo="xp2pInfo"
:rotate="90"
:muted="isMute"
mode="RTC"
:acceptPlayerEvents="true"
soundMode="speaker"
sceneType="live"
:streamQuality="range[index].value"
:minCache="0.2"
:maxCache="0.8"
:fill="true"
orientation="horizontal"
@playsuccess="handlePlaySuccess"
>
</iot-p2p-player-with-mjpg>
<view
v-if="buttonInfo"
:style="{
top: buttonInfo.bottom + 15 + 'px'
}"
class="bg-[rgba(0,0,0,0.35)] rounded-full px-2 py-1.5 fixed right-32"
>
<picker :value="index" mode="selector" :range="range" range-key="name" @change="changeEvent">
<up-icon
:label="range[index].name"
color="#ffffff"
label-color="#ffffff"
labelPos="left"
name="arrow-down-fill"
size="32rpx"
space="16rpx"
></up-icon>
</picker>
</view>
<image
v-if="!isVideoLoaded"
src="https://oss-lock.xhjcn.ltd/mp/background_monitor.png"
class="w-full h-full absolute top-0 left-0"
></image>
<iot-p2p-voice
v-if="deviceInfo"
id="voiceComponent"
:deviceInfo="deviceInfo"
:xp2pInfo="xp2pInfo"
voiceType="Pusher"
>
</iot-p2p-voice>
<view
v-if="isVideoLoaded"
class="fixed bottom-[calc(32rpx+env(safe-area-inset-bottom))] bg-[rgba(0,0,0,0.3)] rounded-xl p-4 shadow-lg left-1/2 -translate-x-1/2 w-622"
>
<view class="flex items-center justify-around mx-15">
<image
@click="isMute = !isMute"
:src="
isMute
? 'https://oss-lock.xhjcn.ltd/mp/icon_mute.png'
: 'https://oss-lock.xhjcn.ltd/mp/icon_not_mute.png'
"
class="w-48 h-48 p-2"
></image>
<image
@click="handleScreenshot"
src="https://oss-lock.xhjcn.ltd/mp/icon_screenshot.png"
class="w-48 h-48 p-2"
></image>
</view>
<view class="flex items-center justify-between text-white mt-2 px-10">
<view class="flex flex-col items-center" @longpress="startVoice" @touchend="stopVoice">
<view class="bg-white w-80 h-80 rounded-full flex items-center justify-center">
<image
:src="
isVoice
? 'https://oss-lock.xhjcn.ltd/mp/icon_microphone.png'
: 'https://oss-lock.xhjcn.ltd/mp/icon_no_microphone.png'
"
class="w-55 h-55"
></image>
</view>
<view class="mt-2 text-center whitespace-nowrap text-xs">长按说话</view>
</view>
<view class="flex flex-col items-center" @click="handleHangUp">
<view class="bg-[#eb292b] w-80 h-80 rounded-full flex items-center justify-center">
<image src="https://oss-lock.xhjcn.ltd/mp/icon_hang_up.png" class="w-60 h-60"></image>
</view>
<view class="mt-2 text-center whitespace-nowrap text-xs">挂断</view>
</view>
<view class="flex flex-col items-center" @click="handleLock">
<view class="bg-[#63b8af] w-80 h-80 rounded-full flex items-center justify-center">
<image
src="https://oss-lock.xhjcn.ltd/mp/icon_lock_white.png"
class="w-60 h-60"
></image>
</view>
<view class="mt-2 text-center whitespace-nowrap text-xs">开锁</view>
</view>
</view>
</view>
<view
v-if="!isVideoLoaded"
class="fixed bottom-[calc(48rpx+env(safe-area-inset-bottom))] w-full flex justify-center"
>
<up-loading-icon
size="70rpx"
:vertical="true"
textSize="28rpx"
text="连接中"
mode="circle"
></up-loading-icon>
</view>
</view>
</template>
<script setup>
import { onMounted, ref } from 'vue'
import { onUnload } from '@dcloudio/uni-app'
import { useBluetoothStore } from '@/stores/bluetooth'
import { useBasicStore } from '@/stores/basic'
import { useUserStore } from '@/stores/user'
import { passthrough } from '@/api/sdk'
import { getLockNetTokenRequest } from '@/api/lock'
const $bluetooth = useBluetoothStore()
const $basic = useBasicStore()
const $user = useUserStore()
const buttonInfo = ref(null)
const onlineToken = ref('0')
const lockId = ref()
const time = ref(0)
const pending = ref(false)
const index = ref(1)
const range = ref([
{ name: '标清', value: 'standard' },
{ name: '高清', value: 'high' },
{ name: '超清', value: 'super' }
])
const deviceInfo = ref()
const xp2pInfo = ref()
const isVoice = ref(false)
const isMute = ref(false)
const isVideoLoaded = ref(false)
onMounted(async () => {
buttonInfo.value = await $basic.getButtonInfo()
const { code, data, message } = await passthrough({
request_method: 'GET',
request_uri: '/api/v1/tencentYun/getDeviceDetailData',
post_args: {
lockId: $bluetooth.currentLockInfo.lockId
}
})
if (code === 0) {
deviceInfo.value = {
deviceId: `${data.productId}/${data.deviceName}`,
productId: data.productId,
deviceName: data.deviceName
}
xp2pInfo.value = data.xp2pInfo
} else {
$basic.backAndToast(message)
}
await getServeTime()
})
onUnload(() => {
const page = getCurrentPages().pop()
const player = page.selectComponent('#playerRef')
if (player.stopAll) {
player.stopAll()
}
})
const changeEvent = e => {
index.value = e.detail.value
}
const handleScreenshot = () => {
const page = getCurrentPages().pop()
const player = page.selectComponent('#playerRef')
player.snapshot().then(res => {
uni.saveImageToPhotosAlbum({
filePath: res.tempImagePath,
success: () => {
uni.showToast({
title: '截图已保存到相册',
icon: 'none'
})
},
fail: () => {
uni.showToast({
title: '保存失败',
icon: 'none'
})
}
})
})
}
const handleHangUp = () => {
const page = getCurrentPages().pop()
const player = page.selectComponent('#playerRef')
if (player.stopAll) {
player.stopAll()
}
uni.navigateBack()
}
const handleLock = () => {
uni.showModal({
title: '提示',
content: '是否确认开锁?',
success: res => {
if (res.confirm) {
openDoorOperate()
}
}
})
}
const getServeTime = async () => {
const { code, data } = await $bluetooth.updateServerTimestamp()
if (code === 0) {
time.value = parseInt((data.date - new Date().getTime()) / 1000, 10)
}
}
const openDoorOperate = async () => {
const timestamp = new Date().getTime()
if (pending.value) {
return
}
const netWork = await $basic.getNetworkType()
if (!netWork) {
return
}
if ($bluetooth.currentLockInfo.appUnlockOnline) {
const result = await getNetToken()
if (!result) {
pending.value = false
return
}
}
uni.showLoading({
title: '开锁中'
})
uni.vibrateLong()
pending.value = true
const openMode = $bluetooth.currentLockInfo.appUnlockOnline ? 1 : 0
const { code } = await $bluetooth.openDoor({
name: $bluetooth.currentLockInfo.lockName,
uid: $user.userInfo.uid.toString(),
openMode,
openTime: parseInt(new Date().getTime() / 1000, 10) + time.value,
onlineToken: onlineToken.value
})
$bluetooth
.syncRecord({
keyId: $bluetooth.keyId.toString(),
uid: $user.userInfo.uid.toString()
})
.then(() => {
$bluetooth.closeBluetoothConnection()
})
// #ifdef MP-WEIXIN
uni.reportEvent('open_door', {
result: code,
duration: new Date().getTime() - timestamp
})
// #endif
if (code === 0) {
uni.showToast({
title: `开门成功`,
icon: 'none'
})
} else if (code === 7) {
uni.showToast({
title: `钥匙过期`,
icon: 'none'
})
} else if (code === 13) {
uni.showToast({
title: `钥匙当前不可用`,
icon: 'none'
})
} else if (code === -1) {
uni.showToast({
title: `开锁失败`,
icon: 'none'
})
}
uni.hideLoading()
pending.value = false
}
const getNetToken = async () => {
const { code, data, message } = await getLockNetTokenRequest({
lockId: lockId.value
})
if (code === 0) {
onlineToken.value = data.token
return true
}
uni.showToast({
title: message,
icon: 'none'
})
return false
}
const startVoice = () => {
uni.vibrateLong()
isVoice.value = true
const page = getCurrentPages().pop()
const voice = page.selectComponent('#voiceComponent')
voice.startVoice()
}
const stopVoice = () => {
isVoice.value = false
const page = getCurrentPages().pop()
const voice = page.selectComponent('#voiceComponent')
voice.stopVoice()
}
const handlePlaySuccess = () => {
isVideoLoaded.value = true
}
</script>
<style lang="scss" scoped>
:deep(.mjpg-player--iot-player) {
height: 100vh !important;
}
</style>