Vue3 + xgplayer-flv 实现智慧园区监控分屏实战(附完整代码)

发布时间:2026/7/15 13:15:41

Vue3 + xgplayer-flv 实现智慧园区监控分屏实战(附完整代码) Vue3 xgplayer-flv 实现智慧园区监控分屏系统开发指南在智慧园区、智慧工地等场景中多路视频监控的实时展示与灵活切换是刚需功能。本文将手把手教你如何基于Vue3和xgplayer-flv插件打造一个支持单屏、四屏、六屏自由切换的专业级监控系统。1. 技术选型与环境搭建1.1 核心组件介绍现代前端监控系统需要解决几个关键问题低延迟播放、多路视频管理和设备联动控制。我们选择的方案组合是Vue3提供响应式数据管理和组件化开发体验xgplayer-flv支持FLV格式的低延迟播放特别适合监控场景Element Plus提供树形设备列表等UI组件1.2 初始化项目使用Vite快速创建Vue3项目npm create vitelatest smart-surveillance --template vue-ts cd smart-surveillance npm install xgplayer xgplayer-flv element-plus在main.ts中引入必要依赖import { createApp } from vue import App from ./App.vue import ElementPlus from element-plus import element-plus/dist/index.css const app createApp(App) app.use(ElementPlus) app.mount(#app)2. 播放器核心实现2.1 基础播放器配置创建components/VideoPlayer.vue组件template div :idplayerId classvideo-container/div /template script setup langts import { onMounted, onBeforeUnmount } from vue import Player from xgplayer import FlvPlayer from xgplayer-flv import xgplayer/dist/index.min.css const props defineProps({ playerId: { type: String, required: true }, videoUrl: { type: String, required: true } }) let player: Player | null null onMounted(() { player new Player({ id: props.playerId, url: props.videoUrl, isLive: true, plugins: [FlvPlayer], autoplay: true, autoplayMuted: true, lang: zh-cn, screenShot: true, playbackRate: [0.5, 0.75, 1, 1.5, 2] }) }) onBeforeUnmount(() { player?.destroy() }) /script style scoped .video-container { width: 100%; height: 100%; background-color: #000; } /style2.2 多屏布局管理在父组件中实现分屏切换逻辑// 分屏类型定义 type LayoutType single | quad | six const layoutType refLayoutType(single) const activePlayers ref{id: string, url: string}[]([]) // 切换布局 const changeLayout (type: LayoutType) { layoutType.value type // 根据布局类型初始化播放器容器 switch(type) { case single: activePlayers.value [{id: player-1, url: }] break case quad: activePlayers.value Array(4).fill(0).map((_,i) ({ id: player-${i1}, url: })) break case six: activePlayers.value Array(6).fill(0).map((_,i) ({ id: player-${i1}, url: })) break } }3. 设备树与视频联动3.1 设备树组件实现使用Element Plus的Tree组件展示监控设备template el-tree :datadeviceTree :propstreeProps node-keyid highlight-current node-clickhandleNodeClick / /template script setup langts interface DeviceNode { id: string label: string isOnline: boolean children?: DeviceNode[] streamUrl?: string } const deviceTree refDeviceNode[]([ { id: area-1, label: 园区A区, isOnline: true, children: [ { id: cam-1-1, label: 东门摄像头, isOnline: true, streamUrl: flv://stream.example.com/cam1 } ] } ]) const treeProps { children: children, label: label } const emit defineEmits([select]) const handleNodeClick (node: DeviceNode) { if (node.streamUrl) { emit(select, node) } } /script3.2 视频分配策略实现将选中的视频分配到当前聚焦的播放器窗口const focusedPlayer refstring|null(null) const players refRecordstring, string({}) const assignVideoToPlayer (playerId: string, url: string) { players.value[playerId] url } const handlePlayerFocus (playerId: string) { focusedPlayer.value playerId } const handleDeviceSelect (device: DeviceNode) { if (focusedPlayer.value device.streamUrl) { assignVideoToPlayer(focusedPlayer.value, device.streamUrl) } }4. 云台控制面板开发4.1 PTZ控制界面为球型摄像头添加方向控制功能template div classptz-control div classptz-row button mousedownstartMove(up) mouseupstopMove i classicon-arrow-up/i /button /div div classptz-row button mousedownstartMove(left) mouseupstopMove i classicon-arrow-left/i /button button mousedownstartMove(right) mouseupstopMove i classicon-arrow-right/i /button /div div classptz-row button mousedownstartMove(down) mouseupstopMove i classicon-arrow-down/i /button /div /div /template script setup langts import { ref } from vue const currentDirection refstring|null(null) let moveInterval: number|null null const startMove (direction: string) { currentDirection.value direction // 实际项目中这里调用API控制摄像头转动 moveInterval setInterval(() { console.log(Moving ${direction}...) }, 100) } const stopMove () { if (moveInterval) { clearInterval(moveInterval) moveInterval null } currentDirection.value null } /script style scoped .ptz-control { display: flex; flex-direction: column; align-items: center; gap: 8px; } .ptz-row { display: flex; gap: 8px; } button { width: 40px; height: 40px; border-radius: 50%; background: #2c3e50; color: white; border: none; cursor: pointer; } /style4.2 变焦与预设位控制// 变焦控制 const zoomIn (cameraId: string) { console.log(Zoom in on ${cameraId}) // 调用API实现变焦 } const zoomOut (cameraId: string) { console.log(Zoom out on ${cameraId}) // 调用API实现变焦 } // 预设位管理 const savePreset (cameraId: string, presetIndex: number) { console.log(Save preset ${presetIndex} for ${cameraId}) } const gotoPreset (cameraId: string, presetIndex: number) { console.log(Go to preset ${presetIndex} on ${cameraId}) }5. 性能优化与错误处理5.1 播放器实例管理const playerInstances refRecordstring, Player({}) const createPlayer (containerId: string, url: string) { if (playerInstances.value[containerId]) { playerInstances.value[containerId].destroy() } playerInstances.value[containerId] new Player({ id: containerId, url, isLive: true, plugins: [FlvPlayer], autoplay: true }) } const destroyAllPlayers () { Object.values(playerInstances.value).forEach(player { player.destroy() }) playerInstances.value {} }5.2 网络中断处理// 监听播放器错误事件 player.on(error, (err) { console.error(Player error:, err) // 显示重试按钮 }) // 自动重试逻辑 const retryPlayback (playerId: string) { const url players.value[playerId] if (url) { createPlayer(playerId, url) } }6. 完整系统集成6.1 主界面布局template div classsurveillance-system div classsidebar DeviceTree selecthandleDeviceSelect / PTZControl v-ifactiveCamera / /div div classmain-content div classlayout-control button clickchangeLayout(single)单屏/button button clickchangeLayout(quad)四屏/button button clickchangeLayout(six)六屏/button /div div classvideo-grid :classlayoutType VideoPlayer v-forplayer in activePlayers :keyplayer.id :player-idplayer.id :video-urlplayer.url focushandlePlayerFocus(player.id) / /div /div /div /template script setup langts // 导入之前定义的组件和逻辑 /script style scoped .surveillance-system { display: flex; height: 100vh; background-color: #f0f2f5; } .sidebar { width: 280px; background-color: #fff; border-right: 1px solid #e8e8e8; padding: 16px; overflow-y: auto; } .main-content { flex: 1; display: flex; flex-direction: column; } .layout-control { padding: 12px; background-color: #fff; border-bottom: 1px solid #e8e8e8; } .video-grid { flex: 1; display: grid; gap: 8px; padding: 8px; background-color: #000; } .video-grid.single { grid-template-columns: 1fr; grid-template-rows: 1fr; } .video-grid.quad { grid-template-columns: 1fr 1fr; grid-template-rows: 1fr 1fr; } .video-grid.six { grid-template-columns: 1fr 1fr 1fr; grid-template-rows: 1fr 1fr; } /style6.2 状态持久化// 保存当前布局和视频分配 const saveLayout () { const layoutData { layoutType: layoutType.value, players: players.value, lastUpdate: new Date().toISOString() } localStorage.setItem(surveillanceLayout, JSON.stringify(layoutData)) } // 恢复布局 const loadLayout () { const saved localStorage.getItem(surveillanceLayout) if (saved) { const layoutData JSON.parse(saved) layoutType.value layoutData.layoutType players.value layoutData.players // 重新创建播放器实例 Object.entries(players.value).forEach(([id, url]) { if (url) createPlayer(id, url) }) } } onMounted(() { loadLayout() })在实际项目中部署这套系统时建议添加WebSocket支持以实现实时状态更新并考虑使用Vuex或Pinia进行更复杂的状态管理。对于大型园区监控系统还可以添加地图视图、报警联动等高级功能。

相关新闻