Files
movecheck/src/pages/visual/index.vue

731 lines
15 KiB
Vue
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="visual-monitoring-page">
<!-- 固定头部 - 有视频时隐藏 -->
<view class="fixed-header">
<text class="header-title">移动式检修车间</text>
</view>
<!-- 内容区域 -->
<view class="tabbar-content">
<!-- <demo /> -->
<view class="no-data-container" v-if="!ezstate">
<view class="no-data-icon">📹</view>
<text class="no-data-text">暂无监控数据</text>
</view>
<!-- 视频播放区域 - 保持16:9比例 -->
<view v-else-if="ezstate" :key="videoData" class="video-wrapper">
<view class="video-content">
<!-- 使用简化版播放器 -->
<EzvizVideoPlayer
ref="playerVideoRef"
:show-debug="debugMode"
@playStateChange="handlePlayStateChange"
></EzvizVideoPlayer>
</view>
</view>
<!-- 视频信息区域 -->
<view v-if="ezstate" class="video-info">
<view class="info-item">
<text class="info-label">📡 设备状态</text>
<text class="info-value online">在线</text>
</view>
<view class="info-item">
<text class="info-label">🎥 分辨率</text>
<text class="info-value">高清</text>
</view>
<view class="info-item">
<text class="info-label">🔊 音频</text>
<text class="info-value">开启</text>
</view>
</view>
<!-- 视频控制按钮 -->
<view v-if="ezstate" class="control-section">
<button @click="handleInitPlayer" class="control-btn init-btn">
🔄 初始化
</button>
<button @click="handleTogglePlay" class="control-btn pause-btn">
{{ isPlaying ? '⏸ 暂停播放' : '▶ 开始播放' }}
</button>
</view>
<!-- API测试按钮 -->
<view class="test-section" v-if="!ezstate">
<button @click="checkDevice" class="test-btn">检查设备状态</button>
<button @click="toggleDebug" class="test-btn">
{{ debugMode ? '关闭调试' : '开启调试' }}
</button>
<button @click="getVideoData" class="test-btn">启动视频播放</button>
</view>
</view>
</view>
</template>
<script>
// 改用简化版播放器
import EzvizVideoPlayer from '@/components/EzvizVideoPlayerSimple.vue'
import tokenManager from '@/utils/ezvizTokenManager.js'
import deviceChecker from '@/utils/ezvizDeviceChecker.js'
export default {
components: {
EzvizVideoPlayer
},
data() {
return {
ezstate:false,
debugMode: true, // 默认开启调试模式
videoLoaded: false,
isRecording: false,
isPlaying: true, // 播放状态
cameraStatus: {
text: '离线',
class: 'offline'
},
recordingStatus: {
text: '未录制',
class: 'inactive'
},
qualityIndex: 1,
qualityOptions: ['低', '中', '高', '超高清'],
durationIndex: 2,
durationOptions: ['5分钟', '10分钟', '30分钟', '1小时', '持续录制'],
autoSave: true,
historyList: [
{
time: '2025-09-29 15:45:33',
duration: '10:30',
size: '125MB'
},
{
time: '2025-09-29 14:20:15',
duration: '5:45',
size: '68MB'
},
{
time: '2025-09-29 13:10:22',
duration: '15:20',
size: '189MB'
}
]
}
},
onLoad() {
console.log('视觉监控页面加载')
this.getVideoData()
},
onShow() {
console.log('📱 视觉监控页面显示,触发页面更新')
// 可以在这里添加重新连接摄像头等逻辑
this.getVideoData()
},
methods: {
// 切换调试模式
toggleDebug() {
this.debugMode = !this.debugMode
console.log('调试模式:', this.debugMode ? '开启' : '关闭')
uni.showToast({
title: this.debugMode ? '调试模式已开启' : '调试模式已关闭',
icon: 'success',
duration: 1500
})
},
// 初始化播放器
async handleInitPlayer() {
console.log('🔄 重新初始化播放器...')
uni.showLoading({
title: '正在初始化...'
})
try {
// 重新获取视频数据并初始化
await this.getVideoData()
uni.hideLoading()
uni.showToast({
title: '初始化成功',
icon: 'success',
duration: 2000
})
// 重置播放状态
this.isPlaying = true
} catch (error) {
uni.hideLoading()
console.error('初始化失败:', error)
uni.showToast({
title: '初始化失败',
icon: 'error',
duration: 2000
})
}
},
// 切换播放/暂停
handleTogglePlay() {
console.log('🎬 切换播放状态:', this.isPlaying ? '暂停' : '播放')
if (this.$refs.playerVideoRef) {
// 调用播放器组件的切换播放方法(状态会通过事件同步)
this.$refs.playerVideoRef.togglePlay()
} else {
console.error('❌ 播放器组件未找到')
uni.showToast({
title: '播放器未就绪',
icon: 'error',
duration: 2000
})
}
},
// 处理播放状态变化(由播放器组件触发)
handlePlayStateChange(isPlaying) {
console.log('📡 播放状态变化:', isPlaying ? '播放中' : '已暂停')
this.isPlaying = isPlaying
},
// 检查设备状态
async checkDevice() {
console.log('🔍 开始检查设备状态...')
const playUrl = "ezopen://open.ys7.com/FT1718031/1.hd.live"
uni.showLoading({
title: '检查设备中...'
})
try {
const result = await deviceChecker.comprehensiveCheck(playUrl)
uni.hideLoading()
if (result.success) {
const status = result.isOnline ? '在线' : '离线'
const message = `设备 ${result.deviceSerial}: ${status}\n设备名: ${result.device.deviceName || '未知'}`
uni.showModal({
title: '设备检查结果',
content: message,
showCancel: false
})
console.log('✅ 设备检查结果:', result)
} else {
uni.showModal({
title: '设备检查失败',
content: result.error,
showCancel: false
})
console.error('❌ 设备检查失败:', result.error)
}
} catch (error) {
uni.hideLoading()
uni.showToast({
title: '检查异常',
icon: 'error',
duration: 3000
})
console.error('设备检查异常:', error)
}
},
async getVideoData() {
console.log('getVideoData')
try {
let ezuikitInfo = {}
// 使用TokenManager自动获取AccessToken
try {
console.log('🔑 开始获取AccessToken...')
const accessToken = await tokenManager.getValidAccessToken()
ezuikitInfo = {
accessToken: accessToken,
play_url: "ezopen://open.ys7.com/FT1718031/1.hd.live"
}
console.log('✅ 使用自动获取的AccessToken:', accessToken.substring(0, 20) + '...')
} catch (error) {
console.error('❌ 自动获取AccessToken失败:', error)
// 如果自动获取失败使用备用token需要手动更新
console.log('🔄 使用备用AccessToken')
ezuikitInfo = {
accessToken: "at.4q22023n62a4knwpcx1yxavda1sfqfo5-3ns0ca16sb-1wgwwc3-aj2mctqys",
play_url: "ezopen://open.ys7.com/FT1718031/1.hd.live"
}
uni.showToast({
title: 'AccessToken自动获取失败使用备用token',
icon: 'none',
duration: 3000
})
}
// 先启用视频状态,让组件渲染
this.ezstate = true
// 等待组件渲染完成后初始化播放器
await this.$nextTick()
// 确保ref存在后再调用
if (this.$refs.playerVideoRef) {
this.$refs.playerVideoRef.initEzuikit(ezuikitInfo)
} else {
console.error('❌ 播放器组件未找到')
uni.showToast({
title: '播放器组件加载失败',
icon: 'error',
duration: 2000
})
}
} catch (error) {
console.error('初始化视频失败:', error)
uni.showToast({
title: '视频初始化失败',
icon: 'none',
duration: 3000
})
}
}
}
}
</script>
<style lang="scss" scoped>
.visual-monitoring-page {
width: 100%;
height: 100%;
background: #f5f6fa;
}
/* 内容区域 */
.tabbar-content {
width: 100%;
height: calc(100vh - 100rpx); /* 减去底部tabbar */
padding: 30rpx;
overflow-y: auto;
display: flex;
flex-direction: column;
gap: 30rpx;
box-sizing: border-box;
}
/* 视频外层容器 */
.video-wrapper {
width: 100%;
height: 200px;
display: flex;
justify-content: center;
align-items: flex-start;
}
/* 视频内容区域 - 保持16:9宽高比不变形 */
.video-content {
width: 100%;
max-width: 100%;
position: relative;
border-radius: 16rpx;
overflow: hidden;
box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.2);
background: #000;
background-color: #0056b3;
/* 使用padding-top技巧保持16:9宽高比 */
&::before {
content: '';
display: block;
padding-top: 56.25%; /* 16:9 = 9/16 = 0.5625 = 56.25% */
}
/* 播放器绝对定位填充容器 */
:deep(.simple-video-player) {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
}
/* 无数据状态 */
.no-data-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 400rpx;
background: linear-gradient(135deg, #f5f7fa 0%, #e3e7f0 100%);
border-radius: 16rpx;
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.08);
}
.no-data-icon {
font-size: 120rpx;
margin-bottom: 20rpx;
opacity: 0.6;
}
.no-data-text {
font-size: 32rpx;
color: #666;
}
/* 视频信息区域 */
.video-info {
display: flex;
gap: 20rpx;
padding: 20rpx 30rpx;
background: white;
border-radius: 12rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.08);
}
.info-item {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
gap: 8rpx;
padding: 15rpx 10rpx;
background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
border-radius: 10rpx;
}
.info-label {
font-size: 24rpx;
color: #666;
white-space: nowrap;
}
.info-value {
font-size: 26rpx;
font-weight: bold;
color: #333;
&.online {
color: #4caf50;
}
}
/* 视频控制按钮区域 */
.control-section {
display: flex;
gap: 20rpx;
padding: 0 10rpx;
}
.control-btn {
flex: 1;
height: 80rpx;
border-radius: 12rpx;
font-size: 28rpx;
font-weight: bold;
color: white;
border: none;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.15);
transition: all 0.3s ease;
}
.init-btn {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}
.init-btn:active {
background: linear-gradient(135deg, #5568d3 0%, #6a4193 100%);
transform: scale(0.98);
}
.pause-btn {
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
}
.pause-btn:active {
background: linear-gradient(135deg, #e082ea 0%, #e4465b 100%);
transform: scale(0.98);
}
.test-section {
padding: 40rpx;
display: flex;
flex-direction: column;
gap: 20rpx;
align-items: center;
}
.test-btn {
background: #007aff;
color: white;
border: none;
border-radius: 8rpx;
padding: 20rpx 40rpx;
font-size: 28rpx;
width: 300rpx;
}
.test-btn:active {
background: #0056b3;
}
.camera-status {
background: white;
border-radius: 12rpx;
padding: 25rpx;
margin-bottom: 20rpx;
box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.05);
display: flex;
gap: 30rpx;
}
.status-item {
flex: 1;
display: flex;
align-items: center;
gap: 20rpx;
}
.status-icon {
font-size: 48rpx;
}
.camera-icon {
color: #666;
}
.recording-icon {
color: #ff4444;
}
.status-info {
display: flex;
flex-direction: column;
gap: 5rpx;
}
.status-label {
font-size: 24rpx;
color: #666;
}
.status-value {
font-size: 28rpx;
font-weight: bold;
&.online {
color: #4caf50;
}
&.offline {
color: #999;
}
&.recording {
color: #ff4444;
}
&.inactive {
color: #666;
}
}
.video-container {
background: white;
border-radius: 12rpx;
margin-bottom: 20rpx;
box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.05);
overflow: hidden;
}
.video-placeholder {
padding: 60rpx;
text-align: center;
}
.placeholder-image {
width: 300rpx;
height: 200rpx;
margin-bottom: 30rpx;
border-radius: 10rpx;
}
.placeholder-text {
display: block;
font-size: 28rpx;
color: #666;
margin-bottom: 30rpx;
}
.connect-button {
background-color: #3f51b5;
color: white;
padding: 20rpx 40rpx;
border-radius: 10rpx;
font-size: 28rpx;
}
.video-player {
padding: 30rpx;
text-align: center;
}
.video-text {
display: block;
font-size: 32rpx;
color: #333;
margin-bottom: 30rpx;
}
.video-controls {
display: flex;
gap: 20rpx;
justify-content: center;
}
.control-button {
background-color: #3f51b5;
color: white;
padding: 15rpx 30rpx;
border-radius: 8rpx;
font-size: 24rpx;
}
.monitoring-settings {
background: white;
border-radius: 15rpx;
padding: 30rpx;
margin-bottom: 20rpx;
box-shadow: 0 4rpx 12rpx rgba(0,0,0,0.1);
}
.settings-header {
margin-bottom: 30rpx;
}
.settings-title {
font-size: 32rpx;
font-weight: bold;
color: #333;
}
.setting-item {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 30rpx;
&:last-child {
margin-bottom: 0;
}
}
.setting-label {
font-size: 28rpx;
color: #333;
}
.picker-view {
display: flex;
align-items: center;
gap: 10rpx;
padding: 15rpx 20rpx;
background-color: #f8f8f8;
border-radius: 8rpx;
border: 2rpx solid #e0e0e0;
}
.picker-arrow {
color: #999;
font-size: 20rpx;
}
.recording-history {
background: white;
border-radius: 15rpx;
box-shadow: 0 4rpx 12rpx rgba(0,0,0,0.1);
overflow: hidden;
}
.history-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 30rpx;
border-bottom: 2rpx solid #f0f0f0;
}
.history-title {
font-size: 32rpx;
font-weight: bold;
color: #333;
}
.clear-button {
background-color: #ff4444;
color: white;
padding: 10rpx 20rpx;
border-radius: 6rpx;
font-size: 24rpx;
}
.history-list {
height: 400rpx;
}
.history-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 30rpx;
border-bottom: 2rpx solid #f0f0f0;
&:last-child {
border-bottom: none;
}
}
.history-info {
display: flex;
flex-direction: column;
gap: 5rpx;
}
.history-time {
font-size: 28rpx;
color: #333;
font-weight: bold;
}
.history-duration {
font-size: 24rpx;
color: #666;
}
.history-actions {
display: flex;
gap: 15rpx;
}
.action-button {
background-color: #3f51b5;
color: white;
padding: 8rpx 16rpx;
border-radius: 6rpx;
font-size: 22rpx;
&.delete {
background-color: #ff4444;
}
}
</style>