144 lines
4.1 KiB
Vue
Raw Normal View History

<script setup>
2025-05-21 15:36:06 +08:00
import { ref, watch, onMounted, onUnmounted, computed } from 'vue';
import { isEmpty, getAssetsFile } from '@/utils';
import { useRoute, useRouter } from 'vue-router';
import Hls from 'hls.js';
const route = useRoute();
const router = useRouter();
const currentDevice = ref(0);
const currentPicture = ref(0);
const videoPlayer = ref(null);
const hls = ref(null);
const loading = ref(false);
const error = ref(false);
const props = defineProps({
title: {
type: String,
required: true,
default: () => '',
validator: (items) => {
return items;
},
},
devices: {
type: Array,
required: true,
default: () => [],
2025-05-21 15:36:06 +08:00
validator: (items) => {
const validItems = items.filter((item) => item.icon === 'camera');
return validItems.every((item) => {
return (
typeof item === 'object' &&
item !== null &&
typeof item.id === 'number' &&
typeof item.name === 'string' &&
typeof item.icon === 'string' &&
typeof item.detail === 'string' &&
(!item.status || typeof item.status === 'number')
);
});
},
},
});
2025-05-21 15:36:06 +08:00
const emit = defineEmits(['changeDevice']);
// 显示天气详情弹窗
const showWeatherDetail = (data) => {
emit('changeDevice', { message: data });
};
// 监听 currentDevice 的变化
watch(currentDevice, (newValue, oldValue) => {
showWeatherDetail(newValue);
// console.log(`count 从 ${oldValue} 变为 ${newValue}`);
});
onUnmounted(() => {
if (hls.value) {
hls.value.destroy();
}
});
onMounted(async () => {
if (Hls.isSupported()) {
hls.value = new Hls();
hls.value.loadSource('https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8');
hls.value.attachMedia(videoPlayer.value);
hls.value.on(Hls.Events.MANIFEST_PARSED, () => {
videoPlayer.value.play();
});
} else if (videoPlayer.value.canPlayType('application/vnd.apple.mpegurl')) {
// Safari原生支持HLS
videoPlayer.value.src = 'https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8';
}
});
</script>
<template>
<el-card style="border-radius: 16px">
<div style="display: flex; justify-content: space-between">
<div style="font-size: 16px; font-weight: bold; text-align: left">{{ title }}</div>
<div>
<div>当前设备状态</div>
<div>
<div class="dot"></div>
<span>正常</span>
</div>
</div>
<div style="color: #999999; line-height: 25px">
当前设备
<el-select v-model="currentDevice" placeholder="Select" size="small" style="width: 160px; margin-left: 10px">
<el-option v-for="item in devices" :key="item.value" :label="item.detail" :value="item.id" />
</el-select>
</div>
</div>
<div style="display: flex; justify-content: space-between; align-items: center">
<div class="video-wrapper">
<video ref="videoPlayer" controls autoplay muted></video>
<div v-if="loading" class="status-message">正在加载直播流...</div>
<div v-if="error" class="status-message error">{{ error }}</div>
</div>
<div class="pictures-wrapper">
<img :src="getAssetsFile('images/smartFarm/goUp.png')" alt="" style="width: 100px" />
<img :src="getAssetsFile('images/smartFarm/testPic1.png')" style="width: 80%; margin: 5px 0" :alt="devices[currentDevice].detail" />
<img :src="getAssetsFile('images/smartFarm/testPic1.png')" style="width: 80%; margin: 5px 0" :alt="devices[currentDevice].detail" />
<img :src="getAssetsFile('images/smartFarm/goDown.png')" alt="" style="width: 100px" />
</div>
</div>
</el-card>
</template>
<style scoped lang="scss">
.video-wrapper {
height: 100%;
margin-top: 30px;
width: 58%;
video {
width: 100%;
border-radius: 16px;
}
}
.pictures-wrapper {
width: 38%;
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: center;
margin-top: 20px;
img {
cursor: pointer;
}
}
.dot {
height: 10px;
width: 10px;
display: inline-block;
border-radius: 90px;
background-color: #25bf82;
}
</style>