feat:完善功能

This commit is contained in:
zjc
2025-02-26 22:18:09 +08:00
parent 5bdb5ea5c6
commit 01e3aabbfc
9 changed files with 456 additions and 108 deletions

View File

@@ -8,3 +8,12 @@ export function getGpsListApi(data) {
params: data params: data
}) })
} }
// GPS状态列表
export function getGpsStatusListApi(data) {
return request({
url: '/fjtcc-api/api/largeScreen/spot/gpsStatusList',
method: 'get',
params: data
})
}

BIN
src/assets/images/all.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

View File

@@ -7,6 +7,7 @@
export {} export {}
declare global { declare global {
const EffectScope: typeof import('vue')['EffectScope'] const EffectScope: typeof import('vue')['EffectScope']
const ElMessage: typeof import('element-plus/es')['ElMessage']
const computed: typeof import('vue')['computed'] const computed: typeof import('vue')['computed']
const createApp: typeof import('vue')['createApp'] const createApp: typeof import('vue')['createApp']
const customRef: typeof import('vue')['customRef'] const customRef: typeof import('vue')['customRef']

View File

@@ -0,0 +1,220 @@
<template>
<div class="z-dialog">
<el-dialog v-model="modelValue" align-center :modal="false" :show-close="false">
<img class="close" src="@/assets/images/close.png" @click="handleClose" />
<title2 title="核心景区监控" />
<ul class="list">
<li
class="item"
:style="{ backgroundImage: `url(${primary})` }"
v-for="(item, index) in list"
:key="index"
@click="handleItem(item)"
>
<div class="item-unfollow" @click.stop="handleCollect(item.id, index)">取消关注</div>
<video class="item-video" :id="'video' + index" muted autoplay :controls="false">
<source type="application/x-mpegURL" />
</video>
<p>
<span> {{ item.cameraName || item.cameraIndexCode }}</span>
</p>
</li>
</ul>
<div class="pagination">
<el-pagination
v-model:current-page="params.pageNum"
:page-size="params.pageSize"
:total="total"
background
layout="prev, pager, next"
@current-change="pageNumChange"
/>
</div>
</el-dialog>
</div>
<video-dialog v-model="videoShow" :src="src" :cameraIndexCode="cameraIndexCode" />
</template>
<script setup>
import { getVideoListApi } from '@/api/home'
import { postVideoCollectApi } from '@/api/monitor'
import primary from '@/assets/images/item-primary.png'
import Hls from 'hls.js'
import pubSub from 'pubsub-js'
let modelValue = defineModel()
let list = ref([])
let hlsRefs = []
let total = ref(0)
let src = ref('')
let cameraIndexCode = ref('')
let videoShow = ref(false)
let params = reactive({
pageNum: 1,
pageSize: 6,
isCollect: 1
})
watch(
() => modelValue.value,
(val) => {
if (val) {
setTimeout(() => {
getVideoList()
}, 1000)
}
}
)
const handleItem = (item) => {
src.value = item.hlsUrl
cameraIndexCode.value = item.cameraIndexCode
videoShow.value = true
}
const handleCollect = async (id, index) => {
await postVideoCollectApi({
id,
isCollect: 0
})
clearHlsRefs()
params.pageNum = 1
getVideoList()
pubSub.publish('videoCollect')
}
const handleClose = () => {
clearHlsRefs()
modelValue.value = false
}
const clearHlsRefs = () => {
if (hlsRefs.length > 0) {
hlsRefs.map((item) => {
item.destroy()
})
hlsRefs = []
}
}
const pageNumChange = () => {
clearHlsRefs()
list.value = []
getVideoList()
}
const getVideoList = async () => {
let res = await getVideoListApi(params)
list.value = res.data
total.value = res.total
nextTick(() => {
list.value.forEach(async (item, index) => {
var video = document.getElementById(`video${index}`)
const hls = new Hls({
enableWorker: false, // 禁用 Worker 来避免额外的线程
enableSoftwareAES: true, // 使用软件解码器以避免硬件解码的额外请求
cache: true, // 启用缓存
maxBufferLength: 10, // 最大缓冲长度(秒)
maxMaxBufferLength: 15, // 缓冲区长度的上限
maxBufferSize: 20 * 1000 * 1000 // 最大缓冲大小(字节)
})
hls.loadSource(item.hlsUrl)
hls.attachMedia(video)
hls.on(Hls.Events.MANIFEST_PARSED, () => {
video.play()
})
hlsRefs.push(hls)
})
})
}
onMounted(() => {})
onUnmounted(() => {})
</script>
<style scoped lang="scss">
.z-dialog {
:deep(.el-dialog) {
width: vw(1864);
padding: vw(8);
background-image: url('@/assets/images/dialog-bg.png') !important;
background-size: 100% 100%;
}
:deep(.el-dialog__header) {
padding-bottom: 0 !important;
}
}
.list {
gap: vw(8);
height: vw(840);
margin-top: vw(30);
display: flex;
flex-wrap: wrap;
align-content: flex-start;
.item {
position: relative;
width: vw(610);
height: vw(420);
padding: vw(12);
box-sizing: border-box;
background-image: url('@/assets/images/item-primary.png');
background-size: 100% 100%;
&-video {
width: 100%;
height: 100%;
object-fit: cover;
}
> p {
position: absolute;
bottom: vw(12);
width: calc(100% - vw(24));
padding: vw(10) 0;
background: rgba(4, 30, 69, 0.72);
> span {
padding-left: vw(10);
font-weight: 400;
font-size: vw(14);
line-height: vw(14);
color: #ffffff;
}
}
&-unfollow {
cursor: pointer;
display: none;
position: absolute;
right: vw(4);
top: vw(4);
z-index: 99;
width: vw(64);
height: vw(30);
text-align: center;
line-height: vw(30);
font-weight: 400;
font-size: vw(12);
color: #ffffff;
background-image: url('@/assets/images/unfollow.png');
background-size: 100% 100%;
}
&:hover {
.item-unfollow {
display: block;
}
}
}
}
.pagination {
padding-top: vw(20);
margin-right: vw(20);
padding-bottom: vw(20);
display: flex;
justify-content: flex-end;
}
.close {
cursor: pointer;
position: absolute;
right: vw(20);
top: vw(20);
width: vw(60);
z-index: 9999;
}
</style>

View File

@@ -1,20 +1,20 @@
<template> <template>
<div class="core-video"> <div class="core-video">
<div class="title">核心景区视频</div> <div class="title">核心景区视频</div>
<div class="btn-all" @click="allShow = true" />
<ul class="list"> <ul class="list">
<li <li
class="item" class="item"
:style="{ backgroundImage: `url(${primary})` }" :style="{ backgroundImage: `url(${primary})` }"
v-for="(item, index) in list" v-for="(item, index) in list"
:key="index" :key="index"
@click.shop="handleItem(item)" @click="handleItem(item)"
> >
<div> <div>
<p class="item-title--primary"> <p class="item-title--primary">
{{ item.cameraName || item.cameraIndexCode }} {{ item.cameraName || item.cameraIndexCode }}
</p> </p>
<div class="item-unfollow">取消关注</div> <div class="item-unfollow" @click.stop="handleUnfollow(item.id, index)">取消关注</div>
<video <video
class="item-img" class="item-img"
:id="'video' + index" :id="'video' + index"
@@ -30,46 +30,42 @@
</ul> </ul>
</div> </div>
<video-dialog v-model="videoShow" :src="src" :cameraIndexCode="cameraIndexCode" /> <video-dialog v-model="videoShow" :src="src" :cameraIndexCode="cameraIndexCode" />
<all-list v-model="allShow" />
</template> </template>
<script setup> <script setup>
import allList from './allList'
import primary from '@/assets/images/item-primary.png' import primary from '@/assets/images/item-primary.png'
import error from '@/assets/images/item-error.png' import error from '@/assets/images/item-error.png'
import { getVideoListApi, postRefreshApi } from '@/api/home'
import Hls from 'hls.js'
import { mode, baseUrl, proBaseUrl } from '@/utils/config' import { getVideoListApi } from '@/api/home'
import { postVideoCollectApi } from '@/api/monitor'
import Hls from 'hls.js'
import pubSub from 'pubsub-js'
let list = ref([]) let list = ref([])
let src = ref('') let src = ref('')
let cameraIndexCode = ref('') let cameraIndexCode = ref('')
let videoShow = ref(false) let videoShow = ref(false)
let allShow = ref(false)
let webRtcServerList = ref([]) let hlsRefs = []
const handleItem = (item) => { const handleItem = (item) => {
src.value = item.hlsUrl src.value = item.hlsUrl
// src.value = item.rtspUrl
cameraIndexCode.value = item.cameraIndexCode cameraIndexCode.value = item.cameraIndexCode
videoShow.value = true videoShow.value = true
} }
const getVideoList = async () => { const getVideoList = async () => {
let res = await getVideoListApi({ let res = await getVideoListApi({
isCollect: 1 isCollect: 1,
pageNum: 1,
pageSize: 5
}) })
console.log(res, 'res')
list.value = res.data list.value = res.data
// nextTick(() => {
// list.value.forEach(async (item, index) => {
// let webRtcServer = new WebRtcStreamer(
// `video${index}`,
// `${mode == 'dev' ? baseUrl : proBaseUrl}/webrtc`
// )
// webRtcServer.connect(item.rtspUrl, '', 'rtptransport=tcp')
// webRtcServerList.value.push(webRtcServer)
// })
// })
nextTick(() => { nextTick(() => {
list.value.forEach(async (item, index) => { list.value.forEach(async (item, index) => {
var video = document.getElementById(`video${index}`) var video = document.getElementById(`video${index}`)
@@ -86,10 +82,39 @@
hls.on(Hls.Events.MANIFEST_PARSED, () => { hls.on(Hls.Events.MANIFEST_PARSED, () => {
video.play() video.play()
}) })
hlsRefs.push(hls)
}) })
}) })
} }
const initList = () => {
// 释放hls实例
if (hlsRefs.length > 0) {
hlsRefs.map((item) => {
item.destroy()
})
hlsRefs = []
}
getVideoList()
}
const onVideoCollect = () => {
pubSub.subscribe('videoCollect', () => {
initList()
})
}
const handleUnfollow = async (id, index) => {
await postVideoCollectApi({
id,
isCollect: 0
})
list.value.splice(index, 1)
hlsRefs[index].destroy()
hlsRefs.splice(index, 1)
}
onMounted(() => { onMounted(() => {
onVideoCollect()
getVideoList() getVideoList()
}) })
</script> </script>
@@ -99,6 +124,7 @@
background-color: transparent !important; background-color: transparent !important;
} }
.core-video { .core-video {
position: relative;
margin: vw(8); margin: vw(8);
width: vw(300); width: vw(300);
border-radius: vw(2); border-radius: vw(2);
@@ -114,6 +140,17 @@
font-weight: 800; font-weight: 800;
color: #fff; color: #fff;
} }
.btn-all {
cursor: pointer;
position: absolute;
right: vw(0);
top: vw(0);
z-index: 999;
width: vw(60);
height: vw(24);
background-image: url('@/assets/images/all.png');
background-size: 100% 100%;
}
.list { .list {
overflow-y: auto; overflow-y: auto;
@@ -147,10 +184,12 @@
padding: vw(10); padding: vw(10);
background-size: 100% 100%; background-size: 100% 100%;
&-unfollow { &-unfollow {
cursor: pointer;
display: none; display: none;
position: absolute; position: absolute;
right: vw(8); right: vw(4);
top: vw(8); top: vw(4);
z-index: 99;
width: vw(64); width: vw(64);
height: vh(24); height: vh(24);
text-align: center; text-align: center;

View File

@@ -12,14 +12,8 @@
<video class="video" ref="videoRef" muted autoplay controls> <video class="video" ref="videoRef" muted autoplay controls>
<source type="application/x-mpegURL" /> <source type="application/x-mpegURL" />
</video> </video>
<!-- <video class="video" id="bigVideo" muted autoplay controls style="object-fit: cover" /> -->
<div class="action-box"> <div class="action-box">
<!-- <div class="action-item">
<img src="@/assets/images/plus.png" alt="" />
<span>变倍</span>
<img src="@/assets/images/minus.png" alt="" />
</div> -->
<div class="action-item"> <div class="action-item">
<img src="@/assets/images/plus.png" title="焦距变大" @click="handleAction(Z00M_IN)" /> <img src="@/assets/images/plus.png" title="焦距变大" @click="handleAction(Z00M_IN)" />
<span>聚焦</span> <span>聚焦</span>
@@ -50,16 +44,16 @@
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import { postVideoControlApi } from '@/api/monitor' import { postVideoControlApi } from '@/api/monitor'
import { mode, baseUrl, proBaseUrl } from '@/utils/config'
const Z00M_IN = 'ZOOM_IN' // 焦距变大 const Z00M_IN = 'ZOOM_IN' // 焦距变大
const Z00M_OUT = 'ZOOM_OUT' // 焦距变小 const Z00M_OUT = 'ZOOM_OUT' // 焦距变小
const ACTION = '0'
const UP = 'UP' // 上转 const UP = 'UP' // 上转
const DOWN = 'DOWN' // 下转 const DOWN = 'DOWN' // 下转
const LEFT = 'LEFT' // 左转 const LEFT = 'LEFT' // 左转
const RIGHT = 'RIGHT' // 右转 const RIGHT = 'RIGHT' // 右转
const STOP = 'STOP' // 停止操作 const STOP = 'STOP' // 停止操作
let ACTION = '0'
let command = ref('')
const props = defineProps({ const props = defineProps({
src: { src: {
@@ -96,26 +90,27 @@
ACTION = '1' ACTION = '1'
} else { } else {
ACTION = '0' ACTION = '0'
command.value = e
} }
await postVideoControlApi({ await postVideoControlApi({
command: e, command: command.value,
action: ACTION, action: ACTION,
cameraIndexCode: props.cameraIndexCode cameraIndexCode: props.cameraIndexCode
}) })
if (e == STOP) {
command.value = ''
}
ElMessage({ ElMessage({
message: '操作成功', message: '操作成功',
type: 'success' type: 'success'
}) })
} }
const handleClose = () => { const handleClose = () => {
// webRtcServer.disconnect()
hlsRef.destroy() hlsRef.destroy()
hlsRef = null hlsRef = null
modelValue.value = false modelValue.value = false
} }
const init = () => { const init = () => {
// webRtcServer = new WebRtcStreamer('bigVideo', `${mode == 'dev' ? baseUrl : proBaseUrl}/webrtc`)
// webRtcServer.connect(props.src)
hlsRef = new Hls({ hlsRef = new Hls({
enableWorker: false, // 禁用 Worker 来避免额外的线程 enableWorker: false, // 禁用 Worker 来避免额外的线程
enableSoftwareAES: true, // 使用软件解码器以避免硬件解码的额外请求 enableSoftwareAES: true, // 使用软件解码器以避免硬件解码的额外请求

View File

@@ -50,7 +50,7 @@
class="video-item" class="video-item"
v-for="(item, index) in videoList" v-for="(item, index) in videoList"
:key="index" :key="index"
@click="handleItemVideo(item.hlsUrl, 100)" @click="handleItemVideo(item.hlsUrl, 100, item.cameraIndexCode)"
> >
<div class="video-item__inner"> <div class="video-item__inner">
<div <div
@@ -73,7 +73,7 @@
autoplay autoplay
:controls="false" :controls="false"
> >
<source src="" type="application/x-mpegURL" /> <source type="application/x-mpegURL" />
</video> </video>
<p class="video-item__title--primary"> <p class="video-item__title--primary">
{{ item.cameraName || item.cameraIndexCode }} {{ item.cameraName || item.cameraIndexCode }}
@@ -84,8 +84,9 @@
<div class="pagination"> <div class="pagination">
<el-pagination <el-pagination
v-model:current-page="params.pageNum" v-model:current-page="params.pageNum"
background :page-size="params.pageSize"
:total="total" :total="total"
background
layout="prev, pager, next" layout="prev, pager, next"
@current-change="currentChange" @current-change="currentChange"
/> />
@@ -94,16 +95,28 @@
<div v-if="videoLog == 2" class="video-detail"> <div v-if="videoLog == 2" class="video-detail">
<div class="video-detail__wrapper"> <div class="video-detail__wrapper">
<video <video class="video-detail__video" ref="videoRef" muted autoplay controls>
class="video-detail__video" <source type="application/x-mpegURL" />
ref="videoRef"
muted
autoplay
controls
style="object-fit: cover"
>
<source src="" type="application/x-mpegURL" />
</video> </video>
<div class="action-box">
<div class="action-item">
<img src="@/assets/images/plus.png" title="焦距变大" @click="handleAction(Z00M_IN)" />
<span>聚焦</span>
<img src="@/assets/images/minus.png" title="焦距变小" @click="handleAction(Z00M_OUT)" />
</div>
<div class="action-item">
<img src="@/assets/images/up.png" title="上转" @click="handleAction(UP)" />
<img src="@/assets/images/down.png" title="下转" @click="handleAction(DOWN)" />
<img
class="pause"
src="@/assets/images/pause.png"
title="停止操作"
@click="handleAction(STOP)"
/>
<img src="@/assets/images/left.png" title="左转" @click="handleAction(LEFT)" />
<img src="@/assets/images/right.png" title="右转" @click="handleAction(RIGHT)" />
</div>
</div>
</div> </div>
<div class="video-right"> <div class="video-right">
<div class="flex justify-between"> <div class="flex justify-between">
@@ -118,7 +131,7 @@
class="item" class="item"
v-for="(item, index) in videoList" v-for="(item, index) in videoList"
:key="index" :key="index"
@click="handleItemVideo(item.hlsUrl, 101)" @click="handleItemVideo(item.hlsUrl, 101, item.handleItemVideo)"
> >
<div> <div>
<p class="item-title--primary"> <p class="item-title--primary">
@@ -144,18 +157,29 @@
</template> </template>
<script setup> <script setup>
import { getVideoListApi, postRefreshApi, getPreviewUrlApi } from '@/api/home' import { getVideoListApi, getPreviewUrlApi } from '@/api/home'
import { import {
getVideoTypeApi, getVideoTypeApi,
getVideoRegionsApi, getVideoRegionsApi,
postVideoRemainApi, postVideoRemainApi,
postVideoControlApi,
postVideoCollectApi postVideoCollectApi
} from '@/api/monitor' } from '@/api/monitor'
import { debounce } from 'lodash' import { debounce } from 'lodash'
import PubSub from 'pubsub-js' import pubSub from 'pubsub-js'
import Hls from 'hls.js' import Hls from 'hls.js'
const Z00M_IN = 'ZOOM_IN' // 焦距变大
const Z00M_OUT = 'ZOOM_OUT' // 焦距变小
const UP = 'UP' // 上转
const DOWN = 'DOWN' // 下转
const LEFT = 'LEFT' // 左转
const RIGHT = 'RIGHT' // 右转
const STOP = 'STOP' // 停止操作
let ACTION = '0'
let command = ref('')
let videoList = ref([]) let videoList = ref([])
let navList = ref([]) let navList = ref([])
let current = ref(0) let current = ref(0)
@@ -169,6 +193,7 @@
let loading = ref(false) let loading = ref(false)
let total = ref(0) let total = ref(0)
let cameraName = ref('') let cameraName = ref('')
let cameraIndexCode = ref('')
let params = reactive({ let params = reactive({
pageNum: 1, pageNum: 1,
pageSize: 6, pageSize: 6,
@@ -176,6 +201,7 @@
businessVideoDisplayPosition: '' businessVideoDisplayPosition: ''
}) })
let hlsRefs = [] let hlsRefs = []
let hlsRef = null
// const postVideoRemain = async () => { // const postVideoRemain = async () => {
// setInterval(() => { // setInterval(() => {
// postVideoRemainApi({ // postVideoRemainApi({
@@ -185,6 +211,7 @@
// } // }
const initVideo = () => { const initVideo = () => {
clearHlsRefs()
nextTick(() => { nextTick(() => {
videoList.value.forEach(async (item, index) => { videoList.value.forEach(async (item, index) => {
const video = document.getElementById(`monitorVideo${index}`) const video = document.getElementById(`monitorVideo${index}`)
@@ -205,12 +232,22 @@
const onInput = debounce((e) => { const onInput = debounce((e) => {
getVideoRegions() getVideoRegions()
}, 500) }, 500)
const clearHlsRefs = () => {
if (hlsRefs.length > 0) {
hlsRefs.map((item) => {
item.destroy()
})
hlsRefs = []
}
}
const currentChange = (e) => { const currentChange = (e) => {
clearHlsRefs()
videoList.value = [] videoList.value = []
getVideoList() getVideoList()
} }
const handleNav = (e) => { const handleNav = (e) => {
if (current.value == e) return if (current.value == e) return
clearHlsRefs()
videoLog.value = 1 videoLog.value = 1
params.pageNum = 1 params.pageNum = 1
videoList.value = [] videoList.value = []
@@ -220,24 +257,46 @@
} }
const handleBack = () => { const handleBack = () => {
videoLog.value = 1 videoLog.value = 1
hlsRef.destroy()
initVideo() initVideo()
} }
const handleItemVideo = (url, type) => { const handleItemVideo = (url, type, code) => {
videoLog.value = 2 videoLog.value = 2
cameraIndexCode.value = code
setTimeout(() => { setTimeout(() => {
const hls = new Hls({ hlsRef = new Hls({
maxBufferLength: 10, // 最大缓冲长度(秒) maxBufferLength: 10, // 最大缓冲长度(秒)
maxMaxBufferLength: 15, // 缓冲区长度的上限 maxMaxBufferLength: 15, // 缓冲区长度的上限
maxBufferSize: 30 * 1000 * 1000 // 最大缓冲大小(字节) maxBufferSize: 30 * 1000 * 1000 // 最大缓冲大小(字节)
}) })
hls.loadSource(url) hlsRef.loadSource(url)
hls.attachMedia(videoRef.value) hlsRef.attachMedia(videoRef.value)
hls.on(Hls.Events.MANIFEST_PARSED, () => { hlsRef.on(Hls.Events.MANIFEST_PARSED, () => {
videoRef.value.play() videoRef.value.play()
}) })
if (type == 100) initVideo() if (type == 100) initVideo()
}, 1000) }, 1000)
} }
const handleAction = async (e) => {
if (e == STOP) {
ACTION = '1'
} else {
ACTION = '0'
command.value = e
}
await postVideoControlApi({
action: ACTION,
command: command.value,
cameraIndexCode: cameraIndexCode.value
})
if (e == STOP) {
command.value = ''
}
ElMessage({
message: '操作成功',
type: 'success'
})
}
const handleRegions = (e) => { const handleRegions = (e) => {
regionList.value[e].show = !regionList.value[e].show regionList.value[e].show = !regionList.value[e].show
} }
@@ -250,7 +309,7 @@
videoSrc.value = res.data.url videoSrc.value = res.data.url
} }
const handleCollect = async (id, status, index) => { const handleCollect = async (id, status, index) => {
let res = await postVideoCollectApi({ await postVideoCollectApi({
id, id,
isCollect: status == 0 ? 1 : 0 isCollect: status == 0 ? 1 : 0
}) })
@@ -259,6 +318,7 @@
} else { } else {
videoList.value[index].isCollect = 0 videoList.value[index].isCollect = 0
} }
pubSub.publish('videoCollect', id)
} }
const getVideoList = async () => { const getVideoList = async () => {
try { try {
@@ -269,7 +329,6 @@
total.value = res.total total.value = res.total
if (res.data.length > 0) { if (res.data.length > 0) {
videoList.value = res.data videoList.value = res.data
console.log(videoList.value, 'videoList.value')
nextTick(() => { nextTick(() => {
videoList.value.forEach(async (x, index) => { videoList.value.forEach(async (x, index) => {
const video = document.getElementById(`monitorVideo${index}`) const video = document.getElementById(`monitorVideo${index}`)
@@ -339,7 +398,7 @@
} }
} }
const onMonitorChange = () => { const onMonitorChange = () => {
monitorChange = PubSub.subscribe('monitorChange', (res, data) => { monitorChange = pubSub.subscribe('monitorChange', (res, data) => {
params.businessScenicArea = data.scenicSpotId params.businessScenicArea = data.scenicSpotId
params.pageNum = 1 params.pageNum = 1
videoList.value = [] videoList.value = []
@@ -354,17 +413,42 @@
onMonitorChange() onMonitorChange()
}) })
onUnmounted(() => { onUnmounted(() => {
// 释放hls实例 clearHlsRefs()
if (hlsRefs.length > 0) { pubSub.unsubscribe(monitorChange)
hlsRefs.map((item) => {
item.destroy()
})
}
PubSub.unsubscribe(monitorChange)
}) })
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.action {
&-box {
margin-top: vh(16);
gap: vw(20);
display: flex;
align-items: center;
justify-content: center;
}
&-item {
padding: vw(16);
display: flex;
align-items: center;
background: #0a4190;
border-radius: vw(8);
> img {
cursor: pointer;
width: vw(34);
height: auto;
}
> span {
margin: 0 vw(16);
font-weight: 400;
font-size: vw(16);
color: #ffffff;
}
.pause {
margin: 0 vw(10);
}
}
}
//背景色设置为透明 //背景色设置为透明
:deep(.el-input__wrapper) { :deep(.el-input__wrapper) {
background-color: rgba(0, 0, 0, 0); background-color: rgba(0, 0, 0, 0);
@@ -625,6 +709,7 @@
width: 100%; width: 100%;
height: vh(406); height: vh(406);
object-fit: cover; object-fit: cover;
// background-color: #000;
} }
&-detail { &-detail {
margin-left: vw(10); margin-left: vw(10);
@@ -655,7 +740,9 @@
} }
&-detail__video { &-detail__video {
width: 100%; width: 100%;
height: vh(880); height: vh(820);
object-fit: contain;
background-color: #000;
} }
&-right { &-right {
margin-left: vw(8); margin-left: vw(8);

View File

@@ -23,12 +23,11 @@
:key="index" :key="index"
@click="handleTab(index)" @click="handleTab(index)"
> >
{{ item.label }}({{ item.value }}) {{ item.label }}({{ item.number }})
</li> </li>
</ul> </ul>
<ul class="list"> <ul class="list">
<li <li
v-if="active == 0"
class="item" class="item"
:class="{ item__active: current === index }" :class="{ item__active: current === index }"
v-for="(item, index) in list" v-for="(item, index) in list"
@@ -50,7 +49,7 @@
</div> </div>
</div> </div>
</li> </li>
<li <!-- <li
v-if="active == 1" v-if="active == 1"
class="item" class="item"
:class="{ item__active: current === index }" :class="{ item__active: current === index }"
@@ -118,7 +117,7 @@
> >
</div> </div>
</div> </div>
</li> </li> -->
</ul> </ul>
</div> </div>
<div id="big-car-ship" class="big-car-ship" /> <div id="big-car-ship" class="big-car-ship" />
@@ -128,7 +127,7 @@
</template> </template>
<script setup> <script setup>
import { getGpsListApi } from '@/api/scenic' import { getGpsListApi, getGpsStatusListApi } from '@/api/scenic'
import closeIcon from '@/assets/images/close.png' import closeIcon from '@/assets/images/close.png'
import carIcon from '@/assets/images/car.png' import carIcon from '@/assets/images/car.png'
@@ -168,7 +167,6 @@
let lng = ref('') let lng = ref('')
let scenicSpotId = ref('') let scenicSpotId = ref('')
let keyword = ref('') let keyword = ref('')
let status = ref('')
let active = ref(0) let active = ref(0)
let current = ref('') let current = ref('')
let scenicChange = null let scenicChange = null
@@ -181,24 +179,25 @@
let tabs = ref([ let tabs = ref([
{ {
label: '所有', label: '所有',
value: '0' value: '',
number: 0
}, },
{ {
label: '在线', label: '行驶',
value: '0' value: '行驶',
number: 0
},
{
label: '静止',
value: '静止',
number: 0
}, },
{ {
label: '离线', label: '离线',
value: '0' value: '离线',
}, number: 0
{
label: '未启用',
value: '0'
} }
]) ])
let onlineList = ref([])
let offlineList = ref([])
let staticList = ref([])
watch( watch(
() => modelValue.value, () => modelValue.value,
@@ -380,6 +379,7 @@
const handleTab = (index) => { const handleTab = (index) => {
if (active.value == index) return if (active.value == index) return
active.value = index active.value = index
getGpsList()
} }
const handleClose = () => { const handleClose = () => {
@@ -393,18 +393,18 @@
const getGpsList = async () => { const getGpsList = async () => {
let res = await getGpsListApi({ let res = await getGpsListApi({
scenicSpotId: scenicSpotId.value,
keyword: keyword.value, keyword: keyword.value,
status: status.value scenicSpotId: scenicSpotId.value,
status: tabs.value[active.value].value
}) })
list.value = res.data list.value = res.data.list
onlineList.value = res.data.filter((item) => item.status == '在线') let statusRes = await getGpsStatusListApi({
offlineList.value = res.data.filter((item) => item.status == '离线') scenicSpotId: scenicSpotId.value
staticList.value = res.data.filter((item) => item.status == '未启用') })
tabs.value[0].value = res.data.length tabs.value[0].number = statusRes.data['全部']
tabs.value[1].value = onlineList.value.length tabs.value[1].number = statusRes.data['行驶']
tabs.value[2].value = offlineList.value.length tabs.value[2].number = statusRes.data['静止']
tabs.value[3].value = staticList.value.length tabs.value[3].number = statusRes.data['离线']
} }
const onInput = debounce((e) => { const onInput = debounce((e) => {

View File

@@ -3,7 +3,7 @@
<ul class="nav"> <ul class="nav">
<li <li
class="nav-item" class="nav-item"
:class="{ active: current == index }" :class="{ active: current === index }"
v-for="(item, index) in routerList" v-for="(item, index) in routerList"
:key="index" :key="index"
@click="handleNav(item, index)" @click="handleNav(item, index)"
@@ -11,20 +11,11 @@
{{ item.name }} {{ item.name }}
</li> </li>
</ul> </ul>
<div id="traffic-map" :class="[routers.length == 0 ? 'traffic-map-big' : 'traffic-map']" /> <div id="traffic-map" :class="[routers.length == 0 ? 'traffic-map-big' : 'traffic-map']" />
<div class="map-box">
<!-- <div class="video-list">
<div class="li">
<vue3VideoPlay v-bind="options" />
</div>
<div class="li">
<vue3VideoPlay v-bind="options" />
</div>
<div class="menu">查看更多</div>
</div> -->
</div>
<div v-if="routers.length > 0" class="list"> <div v-if="routers.length > 0" class="list">
<div class="item" v-for="(item, index) in routers" :key="index"> <div class="item" v-for="(item, index) in routers" :key="index" @click="handleRouter(item)">
<div class="header"> <div class="header">
<div class="header-left"> <div class="header-left">
<img src="@/assets/images/work-icon-xl-1.png" /> <img src="@/assets/images/work-icon-xl-1.png" />
@@ -133,6 +124,13 @@
map.value.centerAndZoom(new BMapGL.Point(e.longitude, e.latitude), e.level) map.value.centerAndZoom(new BMapGL.Point(e.longitude, e.latitude), e.level)
} }
const handleRouter = (e) => {
let location = e.location.split(',')
console.log(location, 'location')
current.value = ''
map.value.centerAndZoom(new BMapGL.Point(location[0], location[1]), 15)
}
const getRouterList = async () => { const getRouterList = async () => {
let res = await getRouterListApi() let res = await getRouterListApi()
routerList.value = res.data routerList.value = res.data
@@ -141,7 +139,6 @@
const getRouters = async () => { const getRouters = async () => {
let res = await getRoutersApi() let res = await getRoutersApi()
routers.value = res.data routers.value = res.data
console.log(routers.value, 'routers')
} }
onMounted(() => { onMounted(() => {