From 988cf99a030dd197a58ac832b690d65089ee6886 Mon Sep 17 00:00:00 2001 From: Ironsp <1522278303@qq.com> Date: Mon, 25 May 2026 17:53:26 +0800 Subject: [PATCH] =?UTF-8?q?feat=20=E6=8A=BD=E5=8F=96hls=20video=E5=B7=A5?= =?UTF-8?q?=E5=85=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Copilot --- src/utils/video.ts | 63 ++++++++++++++++++++++++++++ src/views/equipmentList/list.vue | 70 ++++++++++++++++++++++++++++++-- 2 files changed, 129 insertions(+), 4 deletions(-) create mode 100644 src/utils/video.ts diff --git a/src/utils/video.ts b/src/utils/video.ts new file mode 100644 index 0000000..b166eb7 --- /dev/null +++ b/src/utils/video.ts @@ -0,0 +1,63 @@ +import Hls from 'hls.js'; +let hlsInstance: Hls | null = null; +const { proxy } = getCurrentInstance() as ComponentInternalInstance; + +export const initHls = (monitorVideoUrl, monitorVideoRef) => { + const url = monitorVideoUrl.value; + const video = monitorVideoRef.value; + if (!url || !video) return; + + // 销毁已有实例 + if (hlsInstance) { + try { + hlsInstance.destroy(); + } catch (e) { + // ignore + } + hlsInstance = null; + } + + if (Hls.isSupported()) { + const hls = new Hls(); + hls.loadSource(url); + hls.attachMedia(video); + hls.on(Hls.Events.MANIFEST_PARSED, () => { + // 自动播放(若浏览器允许) + try { + video.play().catch(() => {}); + } catch (e) {} + }); + hlsInstance = hls; + } else if (video.canPlayType('application/vnd.apple.mpegurl')) { + // Safari/原生支持 HLS + video.src = url; + video.addEventListener('loadedmetadata', () => { + try { + video.play().catch(() => {}); + } catch (e) {} + }); + } else { + proxy?.$modal.msgWarning('当前浏览器不支持 HLS 播放'); + } +}; + +export const destroyHls = (monitorVideoRef) => { + try { + if (hlsInstance) { + hlsInstance.destroy(); + hlsInstance = null; + } + } catch (e) {} + const video = monitorVideoRef.value; + if (video) { + // 清理原生 src,避免残留 + try { + video.pause(); + video.removeAttribute('src'); + // 对某些浏览器,需要调用 load() 来重置 media element + if (typeof video.load === 'function') video.load(); + } catch (e) {} + } +}; + +export default { initHls, destroyHls }; diff --git a/src/views/equipmentList/list.vue b/src/views/equipmentList/list.vue index a0ff67a..3be7564 100644 --- a/src/views/equipmentList/list.vue +++ b/src/views/equipmentList/list.vue @@ -138,6 +138,7 @@ import { listDemo, getDemo, delDemo, addDemo, updateDemo } from '@/api/demo/demo import { DemoVO, DemoQuery, DemoForm } from '@/api/demo/demo/types'; import { log } from 'node:console'; import Hls from 'hls.js'; +import { nextTick } from 'vue'; import { useVideosStore } from '@/store/modules/video'; const videosStore = useVideosStore(); @@ -167,6 +168,65 @@ const dialog1 = reactive({ const monitorVideoUrl = ref(''); const monitorVideoRef = ref(); +let hlsInstance: Hls | null = null; + +const initHls = () => { + const url = monitorVideoUrl.value; + const video = monitorVideoRef.value; + if (!url || !video) return; + + // 销毁已有实例 + if (hlsInstance) { + try { + hlsInstance.destroy(); + } catch (e) { + // ignore + } + hlsInstance = null; + } + + if (Hls.isSupported()) { + const hls = new Hls(); + hls.loadSource(url); + hls.attachMedia(video); + hls.on(Hls.Events.MANIFEST_PARSED, () => { + // 自动播放(若浏览器允许) + try { + video.play().catch(() => {}); + } catch (e) {} + }); + hlsInstance = hls; + } else if (video.canPlayType('application/vnd.apple.mpegurl')) { + // Safari/原生支持 HLS + video.src = url; + video.addEventListener('loadedmetadata', () => { + try { + video.play().catch(() => {}); + } catch (e) {} + }); + } else { + proxy?.$modal.msgWarning('当前浏览器不支持 HLS 播放'); + } +}; + +const destroyHls = () => { + try { + if (hlsInstance) { + hlsInstance.destroy(); + hlsInstance = null; + } + } catch (e) {} + const video = monitorVideoRef.value; + if (video) { + // 清理原生 src,避免残留 + try { + video.pause(); + video.removeAttribute('src'); + // 对某些浏览器,需要调用 load() 来重置 media element + if (typeof video.load === 'function') video.load(); + } catch (e) {} + } +}; type MonitorPoint = { x: number; y: number }; const monitorDrawCollecting = ref(false); @@ -329,14 +389,16 @@ const handleSee = async (row?: any) => { Object.assign(form.value, res.data); dialog1.visible = true; dialog1.title = '修改设备'; - videosStore.getVideos(row.id); - console.log('222222222222:', videosStore.hisUrl); + await videosStore.getVideos(row.id); monitorVideoUrl.value = videosStore.hisUrl; + // 等待 DOM 更新后初始化 Hls + await nextTick(); + initHls(); }; const handleMonitorDialogClose = (done: () => void) => { - monitorVideoRef.value?.pause(); - monitorVideoRef.value && (monitorVideoRef.value.currentTime = 0); + // 卸载 Hls 与清理 video + destroyHls(); resetMonitorDraw(); done(); };