类型:开发
描述:
This commit is contained in:
BIN
dist01.zip
BIN
dist01.zip
Binary file not shown.
BIN
dist5.31.zip
BIN
dist5.31.zip
Binary file not shown.
22
package-lock.json
generated
22
package-lock.json
generated
@@ -15,6 +15,7 @@
|
||||
"hls.js": "^1.5.18",
|
||||
"jssip": "^3.10.1",
|
||||
"lodash": "^4.17.21",
|
||||
"mpegts.js": "^1.8.0",
|
||||
"pinia": "^2.2.6",
|
||||
"pinia-plugin-persistedstate": "^4.2.0",
|
||||
"pubsub-js": "^1.9.5",
|
||||
@@ -3201,6 +3202,12 @@
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/es6-promise": {
|
||||
"version": "4.2.8",
|
||||
"resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz",
|
||||
"integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/esbuild": {
|
||||
"version": "0.25.1",
|
||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.1.tgz",
|
||||
@@ -4272,6 +4279,16 @@
|
||||
"ufo": "^1.5.4"
|
||||
}
|
||||
},
|
||||
"node_modules/mpegts.js": {
|
||||
"version": "1.8.0",
|
||||
"resolved": "https://registry.npmjs.org/mpegts.js/-/mpegts.js-1.8.0.tgz",
|
||||
"integrity": "sha512-ZtujqtmTjWgcDDkoOnLvrOKUTO/MKgLHM432zGDI8oPaJ0S+ebPxg1nEpDpLw6I7KmV/GZgUIrfbWi3qqEircg==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"es6-promise": "^4.2.5",
|
||||
"webworkify-webpack": "github:xqq/webworkify-webpack"
|
||||
}
|
||||
},
|
||||
"node_modules/mrmime": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz",
|
||||
@@ -6143,6 +6160,11 @@
|
||||
"url": "https://opencollective.com/webpack"
|
||||
}
|
||||
},
|
||||
"node_modules/webworkify-webpack": {
|
||||
"version": "2.1.5",
|
||||
"resolved": "git+ssh://git@github.com/xqq/webworkify-webpack.git#24d1e719b4a6cac37a518b2bb10fe124527ef4ef",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/which": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
"hls.js": "^1.5.18",
|
||||
"jssip": "^3.10.1",
|
||||
"lodash": "^4.17.21",
|
||||
"mpegts.js": "^1.8.0",
|
||||
"pinia": "^2.2.6",
|
||||
"pinia-plugin-persistedstate": "^4.2.0",
|
||||
"pubsub-js": "^1.9.5",
|
||||
|
||||
@@ -12,6 +12,7 @@ const instance = axios.create({
|
||||
timeout: 100000,
|
||||
headers: {
|
||||
Authorization: mode == 'dev' ? devToken : proToken,
|
||||
'User-Type': '1',
|
||||
'Content-Type': 'application/json;charset=UTF-8'
|
||||
}
|
||||
})
|
||||
|
||||
352
src/components/HlsPlayer/index-bak.vue
Normal file
352
src/components/HlsPlayer/index-bak.vue
Normal file
@@ -0,0 +1,352 @@
|
||||
<template>
|
||||
<div v-show="isActive" class="myVideo-container">
|
||||
<video
|
||||
class="myVideo"
|
||||
ref="videoElement"
|
||||
muted
|
||||
playsinline
|
||||
:controls="false"
|
||||
disablePictureInPicture
|
||||
></video>
|
||||
<!-- <div v-if="loading" class="loading-overlay pointer-events-none">
|
||||
<div class="loading-text">{{ loadingText }}</div>
|
||||
</div> -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Hls from 'hls.js'
|
||||
|
||||
export default {
|
||||
name: 'HlsPlayer',
|
||||
props: {
|
||||
url: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
isActive: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
width: {
|
||||
type: [String, Number],
|
||||
default: '100%'
|
||||
},
|
||||
height: {
|
||||
type: [String, Number],
|
||||
default: '100%'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
hls: null,
|
||||
video: null,
|
||||
loading: false,
|
||||
loadingText: '加载中...',
|
||||
retryCount: 0,
|
||||
maxRetries: 3,
|
||||
isReady: false,
|
||||
playAttempts: 0,
|
||||
maxPlayAttempts: 3,
|
||||
cleanupTimeout: null
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
videoStyle() {
|
||||
const style = {}
|
||||
if (this.width) {
|
||||
style.width = typeof this.width === 'number' ? `${this.width}px` : this.width
|
||||
}
|
||||
if (this.height) {
|
||||
style.height = typeof this.height === 'number' ? `${this.height}px` : this.height
|
||||
}
|
||||
return style
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
isActive: {
|
||||
handler(newValue) {
|
||||
if (newValue) {
|
||||
this.$nextTick(this.initializePlayer)
|
||||
} else {
|
||||
this.immediateCleanup()
|
||||
}
|
||||
},
|
||||
immediate: true
|
||||
},
|
||||
url(newUrl) {
|
||||
// console.log(newUrl,'77777777777777777777777777777777')
|
||||
if (newUrl && this.isActive) {
|
||||
this.initializePlayer()
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.video = this.$refs.videoElement
|
||||
this.registerVideoEvents()
|
||||
},
|
||||
methods: {
|
||||
registerVideoEvents() {
|
||||
const events = ['canplay', 'waiting', 'playing', 'error', 'stalled', 'suspend']
|
||||
|
||||
events.forEach((event) => {
|
||||
this.video.addEventListener(event, this.handleVideoEvent)
|
||||
})
|
||||
},
|
||||
|
||||
handleVideoEvent(event) {
|
||||
switch (event.type) {
|
||||
case 'canplay':
|
||||
this.isReady = true
|
||||
break
|
||||
case 'waiting':
|
||||
this.showLoadingIndicator('缓冲中...')
|
||||
break
|
||||
case 'playing':
|
||||
this.loading = false
|
||||
break
|
||||
case 'error':
|
||||
this.handlePlaybackError()
|
||||
break
|
||||
case 'stalled':
|
||||
this.showLoadingIndicator('播放卡顿...')
|
||||
break
|
||||
case 'suspend':
|
||||
this.cleanupNetworkResources()
|
||||
break
|
||||
}
|
||||
},
|
||||
|
||||
immediateCleanup() {
|
||||
// 立即停止网络请求
|
||||
if (this.hls) {
|
||||
this.hls.stopLoad()
|
||||
this.hls.detachMedia()
|
||||
}
|
||||
|
||||
// 延迟完全清理以避免播放卡顿
|
||||
this.cleanupTimeout = setTimeout(() => {
|
||||
// 仅在不重新初始化播放器时才调用 fullCleanup
|
||||
if (!this.url || !this.isActive) {
|
||||
this.fullCleanup()
|
||||
}
|
||||
}, 1000)
|
||||
},
|
||||
|
||||
fullCleanup() {
|
||||
if (this.hls) {
|
||||
this.hls.destroy()
|
||||
this.hls = null
|
||||
}
|
||||
|
||||
if (this.video) {
|
||||
this.video.pause()
|
||||
this.video.removeAttribute('src')
|
||||
this.video.load()
|
||||
}
|
||||
|
||||
this.loading = false
|
||||
this.retryCount = 0
|
||||
this.isReady = false
|
||||
this.playAttempts = 0
|
||||
|
||||
if (this.cleanupTimeout) {
|
||||
clearTimeout(this.cleanupTimeout)
|
||||
}
|
||||
},
|
||||
|
||||
initializePlayer() {
|
||||
if (!this.isActive || !this.url) return
|
||||
|
||||
// 如果是重新初始化播放器,先清理已存在的资源
|
||||
if (this.hls) {
|
||||
this.immediateCleanup()
|
||||
}
|
||||
this.hls = new Hls({
|
||||
// 内存优化配置
|
||||
maxBufferSize: 0, // 降低缓冲区大小(15MB)
|
||||
maxBufferLength: 0.1, // 更小的缓冲窗口
|
||||
liveSyncDuration: 1, // 紧跟直播点
|
||||
liveMaxLatencyDuration: 5, // 最大延迟5秒
|
||||
liveDurationInfinity: true,
|
||||
lowLatencyMode: true, // 启用低延迟模式
|
||||
maxMaxBufferLength: 60,
|
||||
backBufferLength: 1, // 减少保留的缓冲数据
|
||||
manifestLoadingTimeOut: 10000,
|
||||
manifestLoadingMaxRetry: 5,
|
||||
fragLoadingTimeOut: 5000,
|
||||
fragLoadingMaxRetry: 2,
|
||||
enableWorker: true, // 启用Web Worker
|
||||
recycleVideoFrames: true, // 启用帧回收
|
||||
|
||||
startLevel: -1,
|
||||
autoStartLoad: true,
|
||||
maxBufferHole: 2, // 允许更大的时间缺口
|
||||
highBufferWatchdogPeriod: 4, // 延长监控周期
|
||||
nudgeMaxRetry: 5, // 增加微调重试次数
|
||||
nudgeOffset: 0.05, // 微调步长(秒)
|
||||
jumpGaps: false,
|
||||
stallThreshold: 1000
|
||||
})
|
||||
|
||||
this.hls.attachMedia(this.video)
|
||||
this.hls.loadSource(this.url)
|
||||
|
||||
// 事件处理
|
||||
this.hls.on(Hls.Events.MANIFEST_PARSED, () => {
|
||||
this.loading = false
|
||||
this.retryCount = 0
|
||||
this.safePlay()
|
||||
})
|
||||
this.hls.on(Hls.Events.ERROR, (event, data) => {
|
||||
console.log('核心视频错误',data.type)
|
||||
// this.hls.startLoad(); //重连
|
||||
if (data.type === Hls.ErrorTypes.BUFFER_STALLED_ERROR) {
|
||||
console.error('缓冲停滞错误,尝试重新加载视频');
|
||||
this.hls.startLoad(); // 尝试重新加载视频
|
||||
}
|
||||
this.handleHlsError(data)
|
||||
})
|
||||
|
||||
this.hls.on(Hls.Events.BUFFER_EOS, () => {
|
||||
this.cleanupNetworkResources()
|
||||
})
|
||||
},
|
||||
initVideo() {
|
||||
this.beforeDestroy()
|
||||
this.hls = new Hls({
|
||||
maxBufferLength: 30, // 最大缓冲长度(秒)
|
||||
maxMaxBufferLength: 15, // 缓冲区长度的上限
|
||||
maxBufferSize: 30 * 1000 * 1000 // 最大缓冲大小(字节)
|
||||
})
|
||||
this.hls.attachMedia(this.video)
|
||||
this.hls.loadSource(this.url)
|
||||
|
||||
// 事件处理
|
||||
this.hls.on(Hls.Events.MANIFEST_PARSED, () => {
|
||||
this.loading = false
|
||||
this.retryCount = 0
|
||||
this.safePlay()
|
||||
})
|
||||
this.hls.on(Hls.Events.ERROR, (event, data) => {
|
||||
this.hls.startLoad(); //重连
|
||||
this.handleHlsError(data)
|
||||
})
|
||||
|
||||
this.hls.on(Hls.Events.BUFFER_EOS, () => {
|
||||
this.cleanupNetworkResources()
|
||||
})
|
||||
},
|
||||
cleanupNetworkResources() {
|
||||
// 清理已播放的缓冲数据
|
||||
if (this.hls && this.video) {
|
||||
try {
|
||||
const currentTime = this.video.currentTime
|
||||
this.hls.mediaBuffer = null
|
||||
if (this.hls.bufferTimer) {
|
||||
clearInterval(this.hls.bufferTimer)
|
||||
}
|
||||
this.hls.flushBuffer()
|
||||
this.video.currentTime = currentTime
|
||||
} catch (e) {
|
||||
console.warn('Buffer cleanup error:', e)
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
handleHlsError(data) {
|
||||
// console.error('HLS Error:', data)
|
||||
if (data.fatal) {
|
||||
switch (data.type) {
|
||||
case Hls.ErrorTypes.NETWORK_ERROR:
|
||||
this.retryLoad()
|
||||
break
|
||||
case Hls.ErrorTypes.MEDIA_ERROR:
|
||||
this.hls.recoverMediaError()
|
||||
break
|
||||
default:
|
||||
this.fullCleanup()
|
||||
break
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
retryLoad() {
|
||||
if (this.retryCount < this.maxRetries) {
|
||||
this.retryCount++
|
||||
this.showLoadingIndicator(`重试第 ${this.retryCount} 次...`)
|
||||
setTimeout(() => this.initializePlayer(), 2000)
|
||||
} else {
|
||||
this.showLoadingIndicator('视频加载失败')
|
||||
this.fullCleanup()
|
||||
}
|
||||
},
|
||||
|
||||
showLoadingIndicator(text) {
|
||||
this.loading = true
|
||||
this.loadingText = text
|
||||
},
|
||||
|
||||
async safePlay() {
|
||||
if (!this.video || this.playAttempts >= this.maxPlayAttempts) return
|
||||
|
||||
try {
|
||||
this.playAttempts++
|
||||
if (this.video.readyState >= 2) {
|
||||
await this.video.play()
|
||||
this.playAttempts = 0
|
||||
} else {
|
||||
setTimeout(this.safePlay, 500)
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('播放失败:', error)
|
||||
setTimeout(this.safePlay, 1000)
|
||||
}
|
||||
}
|
||||
},
|
||||
beforeDestroy() {
|
||||
// 移除所有事件监听
|
||||
if (this.video) {
|
||||
const events = ['canplay', 'waiting', 'playing', 'error', 'stalled', 'suspend']
|
||||
events.forEach((event) => {
|
||||
this.video.removeEventListener(event, this.handleVideoEvent)
|
||||
})
|
||||
}
|
||||
this.fullCleanup()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.myVideo-container {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.myVideo {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
// aspect-ratio: 16/9;
|
||||
/* border: 1px solid #ccc; */
|
||||
// border-radius: vw(5);
|
||||
}
|
||||
|
||||
.loading-overlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0, 0, 0, 0.3);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.loading-text {
|
||||
color: white;
|
||||
padding: vw(10);
|
||||
border-radius: vw(4);
|
||||
background: rgba(0, 0, 0, 0.6);
|
||||
}
|
||||
</style>
|
||||
@@ -2,6 +2,7 @@
|
||||
<div v-show="isActive" class="myVideo-container">
|
||||
<video
|
||||
class="myVideo"
|
||||
id="video"
|
||||
ref="videoElement"
|
||||
muted
|
||||
playsinline
|
||||
@@ -16,7 +17,7 @@
|
||||
|
||||
<script>
|
||||
import Hls from 'hls.js'
|
||||
|
||||
import mpegtsjs from 'mpegts.js'
|
||||
export default {
|
||||
name: 'HlsPlayer',
|
||||
props: {
|
||||
@@ -162,6 +163,27 @@
|
||||
if (this.hls) {
|
||||
this.immediateCleanup()
|
||||
}
|
||||
if(this.url.startsWith('ws')){
|
||||
const videoElement = document.getElementById('video')
|
||||
const player = mpegtsjs.createPlayer({
|
||||
url: this.url,
|
||||
type: 'flv',
|
||||
isLive: true,
|
||||
hasAudio: false
|
||||
})
|
||||
player.attachMediaElement(videoElement)
|
||||
player.load()
|
||||
player.play()
|
||||
|
||||
// 错误处理和重连机制
|
||||
player.on('error', (err) => {
|
||||
// 3 秒后尝试重新加载
|
||||
setTimeout(() => {
|
||||
player.load()
|
||||
player.play()
|
||||
}, 3000)
|
||||
})
|
||||
}else{
|
||||
this.hls = new Hls({
|
||||
// 内存优化配置
|
||||
maxBufferSize: 0, // 降低缓冲区大小(15MB)
|
||||
@@ -211,6 +233,8 @@
|
||||
this.hls.on(Hls.Events.BUFFER_EOS, () => {
|
||||
this.cleanupNetworkResources()
|
||||
})
|
||||
}
|
||||
|
||||
},
|
||||
initVideo() {
|
||||
this.beforeDestroy()
|
||||
|
||||
@@ -59,7 +59,6 @@
|
||||
watch(
|
||||
() => props.dataList,
|
||||
(newVal) => {
|
||||
|
||||
aIndex+=1
|
||||
|
||||
if(aIndex>=3&&!newVal.length){
|
||||
@@ -67,22 +66,8 @@
|
||||
}
|
||||
if (newVal.length > 0) {
|
||||
|
||||
console.log(colorList.value,'colorList')
|
||||
condShow.value = 2
|
||||
nextTick(() => {
|
||||
init()
|
||||
// defaultCofig.legend.formatter = (name) => {
|
||||
// let percent = props.dataList.find((item) => item.name == name).value
|
||||
// return name + '\u3000' + `${percent}%`
|
||||
// }
|
||||
// defaultCofig.series[0].data = props.dataList
|
||||
// defaultCofig.series[0].label.formatter = () => {
|
||||
// return `{value|${props.total}}` + '\n' + `{name|${props.label} }`
|
||||
// }
|
||||
// setOption({
|
||||
// ...defaultCofig,
|
||||
// ...props.config
|
||||
// })
|
||||
|
||||
})
|
||||
}else{
|
||||
@@ -93,6 +78,10 @@
|
||||
{ immediate: true }
|
||||
)
|
||||
const init = ()=>{
|
||||
if(condShow.value===2){
|
||||
return;
|
||||
}
|
||||
condShow.value = 2
|
||||
props.dataList.forEach((item,index)=>{
|
||||
|
||||
if(item.name=='负面'){
|
||||
@@ -167,10 +156,36 @@
|
||||
defaultCofig.series[0].label.formatter = () => {
|
||||
return `{value|${props.total}}` + '\n' + `{name|${props.label} }`
|
||||
}
|
||||
const chart = setOption({
|
||||
...defaultCofig,
|
||||
...props.config
|
||||
})
|
||||
chart.on('legendselectchanged', function (e) {
|
||||
console.log(e,'e')
|
||||
var echartsArr = [];
|
||||
for (let key in e.selected) {
|
||||
if (e.selected[key]) {
|
||||
echartsArr.push(key)
|
||||
}
|
||||
}
|
||||
var echartsNum = 0;
|
||||
props.dataList.forEach(item => {
|
||||
if(echartsArr.includes(item.name)){
|
||||
echartsNum += parseFloat(item.value)
|
||||
}
|
||||
})
|
||||
defaultCofig.series[0].label.formatter = `{value|${parseInt(echartsNum/100*props.total)}}` + '\n' + `{name|${props.label}}`;
|
||||
console.log(111111)
|
||||
console.log({
|
||||
...defaultCofig,
|
||||
...props.config
|
||||
})
|
||||
console.log(222222)
|
||||
setOption({
|
||||
...defaultCofig,
|
||||
...props.config
|
||||
})
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -49,8 +49,8 @@
|
||||
|
||||
</div>
|
||||
<div class="action-item">
|
||||
<div class="video-follow" @click="handleCollectAdd(cameraIndexCode, isDiy, index)" v-if="isDiy==0">收藏</div>
|
||||
<div class="video-follow" @click="handleCollectAdd(cameraIndexCode, isDiy, index)" v-else="isDiy==1">取消收藏</div>
|
||||
<div class="video-follow" @click="handleCollectAdd()" v-if="isDayCurr==0">收藏</div>
|
||||
<div class="video-follow" @click="handleCollectAdd()" v-if="isDayCurr==1">取消收藏</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@@ -103,8 +103,8 @@
|
||||
watch(
|
||||
() =>modelValue.value,
|
||||
(val) => {
|
||||
// colletCond.value = props.isCollect
|
||||
// isDayCurr.value = props.isDiy
|
||||
colletCond.value = props.isCollect
|
||||
isDayCurr.value = props.isDiy
|
||||
console.log(props.isDiy,'val[0].value')
|
||||
|
||||
},
|
||||
@@ -114,7 +114,7 @@
|
||||
)
|
||||
const emit = defineEmits(['isDiyChange']);
|
||||
// 收藏
|
||||
const handleCollectAdd = async (id, status, index) => {
|
||||
const handleCollectAdd = async () => {
|
||||
await getColletDiyApi({
|
||||
cameraIndexCode:props.cameraIndexCode,
|
||||
isDiy: props.isDiy == 0 ? 1 : 0
|
||||
|
||||
@@ -13,9 +13,9 @@ export function useEchart() {
|
||||
const setOption = (params, update = false) => {
|
||||
initChart()
|
||||
chart.setOption(params, update)
|
||||
return chart;
|
||||
}
|
||||
const clearOption = () => {
|
||||
console.log('clearooooooooooooooooo')
|
||||
// 将series设置为空数组,可以清空图表内容
|
||||
chart.setOption({
|
||||
series:[]
|
||||
|
||||
@@ -10,25 +10,14 @@
|
||||
:key="index"
|
||||
@click="handleItem(item)"
|
||||
>
|
||||
<HlsPlayer :url="item.hlsUrl" />
|
||||
<div class="item-unfollow" @click.stop="handleUnfollow(item.cameraIndexCode, index)">取消关注</div>
|
||||
<!-- <div>
|
||||
<p class="item-title--primary">
|
||||
{{ item.cameraName || item.cameraIndexCode }}
|
||||
</p>
|
||||
|
||||
<video
|
||||
class="item-img"
|
||||
:id="'video' + index"
|
||||
muted
|
||||
autoplay
|
||||
:controls="false"
|
||||
:src="item.hlsUrl"
|
||||
controlsList="nodownload"
|
||||
>
|
||||
<source src="" type="application/x-mpegURL" />
|
||||
</video>
|
||||
</div> -->
|
||||
></video>
|
||||
<div class="item-unfollow" @click.stop="handleUnfollow(item.cameraIndexCode, index)">取消关注</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
@@ -50,6 +39,7 @@
|
||||
|
||||
import { useWebSocket } from '@/hooks/socket'
|
||||
import { mode, socketBaseUrl, proSocketBaseUrl } from '@/utils/config'
|
||||
import mpegtsjs from "mpegts.js";
|
||||
|
||||
const { dataRes } = useWebSocket(
|
||||
`${mode == 'dev' ? socketBaseUrl : proSocketBaseUrl}/ws/securityAlerts`
|
||||
@@ -73,7 +63,6 @@ let isCollect = ref(0)
|
||||
let timer = null
|
||||
let isDiy = ref(0)
|
||||
const handleItem = (item) => {
|
||||
console.log(item,'1111111111111111111111111')
|
||||
src.value = item.hlsUrl
|
||||
cameraIndexCode.value = item.cameraIndexCode
|
||||
isCollect.value = item.isCollect
|
||||
@@ -82,12 +71,12 @@ let isCollect = ref(0)
|
||||
}
|
||||
|
||||
const postVideoRemain = () => {
|
||||
timer = setInterval(() => {
|
||||
if(!list.value.length) return false;
|
||||
postVideoRemainApi({
|
||||
cameraIndexCode: list.value.map((item) => item.cameraIndexCode)
|
||||
})
|
||||
}, 1500)
|
||||
// timer = setInterval(() => {
|
||||
// if(!list.value.length) return false;
|
||||
// postVideoRemainApi({
|
||||
// cameraIndexCode: list.value.map((item) => item.cameraIndexCode)
|
||||
// })
|
||||
// }, 1500)
|
||||
}
|
||||
|
||||
const getPreviewUrl = async (code) => {
|
||||
@@ -104,28 +93,32 @@ let isCollect = ref(0)
|
||||
pageSize: 5
|
||||
})
|
||||
list.value = res.data
|
||||
postVideoRemain()
|
||||
if (timer) clearInterval(timer)
|
||||
// console.log(list.value,'list.valuelist.valuelist.valuelist.value')
|
||||
// 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)
|
||||
// })
|
||||
// })
|
||||
nextTick(() => {
|
||||
list.value.forEach(async (item, index) => {
|
||||
var videoElement = document.getElementById(`video${index}`)
|
||||
const player = mpegtsjs.createPlayer({
|
||||
url: item.hlsUrl,
|
||||
type: 'flv',
|
||||
isLive: true,
|
||||
hasAudio: false
|
||||
})
|
||||
player.attachMediaElement(videoElement)
|
||||
player.load()
|
||||
player.play()
|
||||
|
||||
// 错误处理和重连机制
|
||||
player.on('error', (err) => {
|
||||
console.error('播放器错误:', err)
|
||||
// 3 秒后尝试重新加载
|
||||
setTimeout(() => {
|
||||
player.load()
|
||||
player.play()
|
||||
}, 3000)
|
||||
})
|
||||
hlsRefs.push(player)
|
||||
})
|
||||
})
|
||||
}
|
||||
watch(
|
||||
() => list.value,
|
||||
|
||||
@@ -16,7 +16,7 @@ export const useScenicStore = defineStore('scenic', () => {
|
||||
infoList: [
|
||||
{ name: '游玩舒适度', type: 0, value: '空闲' },
|
||||
// { name: '景区安全', type: 0, value: '安全' },
|
||||
{ name: '通景交通', type: 0, value: '通畅' },
|
||||
{ name: '交通拥堵度', type: 0, value: '通畅' },
|
||||
{ name: '停车场负荷', type: 0, value: '空闲' }
|
||||
]
|
||||
})
|
||||
|
||||
@@ -93,6 +93,7 @@
|
||||
import Hls from 'hls.js'
|
||||
import emptyIco from '@/assets/images/n-icon.png'
|
||||
import { debounce } from 'lodash'
|
||||
import mpegtsjs from "mpegts.js";
|
||||
const Z00M_IN = 'ZOOM_IN' // 焦距变大
|
||||
const Z00M_OUT = 'ZOOM_OUT' // 焦距变小
|
||||
const UP = 'UP' // 上转
|
||||
@@ -287,6 +288,26 @@
|
||||
it.videos.forEach((item,index)=>{
|
||||
setTimeout(() => {
|
||||
const video = document.getElementById(`monitorVideo${item.cameraIndexCode}`)
|
||||
if(item.hlsUrl.startsWith('ws')){
|
||||
const player = mpegtsjs.createPlayer({
|
||||
url: item.hlsUrl,
|
||||
type: 'flv',
|
||||
isLive: true,
|
||||
hasAudio: false
|
||||
})
|
||||
player.attachMediaElement(video)
|
||||
player.load()
|
||||
player.play()
|
||||
|
||||
// 错误处理和重连机制
|
||||
player.on('error', (err) => {
|
||||
// 3 秒后尝试重新加载
|
||||
setTimeout(() => {
|
||||
player.load()
|
||||
player.play()
|
||||
}, 3000)
|
||||
})
|
||||
}else{
|
||||
if(item.hlsUrl){
|
||||
const hls = new Hls({
|
||||
maxBufferLength: 10, // 最大缓冲长度(秒)
|
||||
@@ -300,6 +321,8 @@
|
||||
})
|
||||
hlsRefs.push(hls)
|
||||
}
|
||||
}
|
||||
|
||||
}, 1000)
|
||||
|
||||
})
|
||||
@@ -318,18 +341,18 @@
|
||||
)
|
||||
// 更新视频
|
||||
const postVideoRemain = async () => {
|
||||
timer = setInterval(() => {
|
||||
clearInterval(timer)
|
||||
videoList.value.forEach((items,index)=>{
|
||||
setTimeout(()=>{
|
||||
postVideoRemainApi({
|
||||
cameraIndexCode: items.videos.map((item) => item.cameraIndexCode)
|
||||
})
|
||||
},1500)
|
||||
|
||||
})
|
||||
|
||||
}, 1500)
|
||||
// timer = setInterval(() => {
|
||||
// clearInterval(timer)
|
||||
// videoList.value.forEach((items,index)=>{
|
||||
// setTimeout(()=>{
|
||||
// postVideoRemainApi({
|
||||
// cameraIndexCode: items.videos.map((item) => item.cameraIndexCode)
|
||||
// })
|
||||
// },1500)
|
||||
//
|
||||
// })
|
||||
//
|
||||
// }, 1500)
|
||||
}
|
||||
const getVideoRegions = async () => {
|
||||
let res = await getVideoRegionsApi({
|
||||
|
||||
@@ -138,7 +138,23 @@
|
||||
} else {
|
||||
params.series[0].data = getSeriesData()
|
||||
}
|
||||
setOption(params)
|
||||
const chart = setOption(params)
|
||||
chart.on('legendselectchanged', function (e) {
|
||||
var echartsArr = [];
|
||||
for (let key in e.selected) {
|
||||
if (e.selected[key]) {
|
||||
echartsArr.push(key)
|
||||
}
|
||||
}
|
||||
var echartsNum = 0;
|
||||
props.list.forEach(item => {
|
||||
if(echartsArr.includes(item.name)){
|
||||
echartsNum += parseInt(item.count)
|
||||
}
|
||||
})
|
||||
params.series[0].label.formatter = `{label|拥堵次数}` + '\n' + `{value|${echartsNum}}`;
|
||||
setOption(params);
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(() => {})
|
||||
|
||||
@@ -129,8 +129,10 @@
|
||||
</video>
|
||||
<div class="action-box">
|
||||
<div class="action-item">
|
||||
<span class="item-sc" @click="handleCollect(thisVideo.cameraIndexCode, thisVideo.isCollect, index)" v-if="thisVideo.isCollect==0">关注</span>
|
||||
<span class="item-sc" @click="handleCollect(thisVideo.cameraIndexCode, thisVideo.isCollect, index)" v-else="thisVideo.isCollect==1">取消关注</span>
|
||||
<span class="item-sc" @click="handleCollect(thisVideo.cameraIndexCode, thisVideo.isCollect, index)"
|
||||
v-if="thisVideo.isCollect==0">关注</span>
|
||||
<span class="item-sc" @click="handleCollect(thisVideo.cameraIndexCode, thisVideo.isCollect, index)"
|
||||
v-else="thisVideo.isCollect==1">取消关注</span>
|
||||
</div>
|
||||
<div class="action-item">
|
||||
<img src="@/assets/images/plus.png" title="焦距变大" @click="handleAction(Z00M_IN)"/>
|
||||
@@ -150,8 +152,10 @@
|
||||
<img src="@/assets/images/right.png" title="右转" @click="handleAction(RIGHT)"/>
|
||||
</div>
|
||||
<div class="action-item">
|
||||
<span class="item-sc" @click="handleCollectAdd(thisVideo.cameraIndexCode, thisVideo.isDiy, index)" v-if="thisVideo.isDiy==0">收藏</span>
|
||||
<span class="item-sc" @click="handleCollectAdd(thisVideo.cameraIndexCode, thisVideo.isDiy, index)" v-else="thisVideo.isDiy==1">取消收藏</span>
|
||||
<span class="item-sc" @click="handleCollectAdd(thisVideo.cameraIndexCode, thisVideo.isDiy, index)"
|
||||
v-if="thisVideo.isDiy==0">收藏</span>
|
||||
<span class="item-sc" @click="handleCollectAdd(thisVideo.cameraIndexCode, thisVideo.isDiy, index)"
|
||||
v-else="thisVideo.isDiy==1">取消收藏</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -195,6 +199,7 @@
|
||||
|
||||
<script setup>
|
||||
import {getVideoListApi, getPreviewUrlApi, getColletListApi, getColletDiyListApi, getColletDiyApi} from '@/api/home'
|
||||
import mpegtsjs from 'mpegts.js'
|
||||
import {
|
||||
getVideoTypeApi,
|
||||
getVideoRegionsApi,
|
||||
@@ -206,6 +211,7 @@
|
||||
import {debounce} from 'lodash'
|
||||
import pubSub from 'pubsub-js'
|
||||
import Hls from 'hls.js'
|
||||
import HlsPlayer from "@/components/HlsPlayer/index.vue";
|
||||
|
||||
const Z00M_IN = 'ZOOM_IN' // 焦距变大
|
||||
const Z00M_OUT = 'ZOOM_OUT' // 焦距变小
|
||||
@@ -249,30 +255,75 @@
|
||||
let timer = null
|
||||
|
||||
const postVideoRemain = async () => {
|
||||
if(!videoList.value.length) return false;
|
||||
timer = setInterval(() => {
|
||||
postVideoRemainApi({
|
||||
cameraIndexCode: videoList.value.map((item) => item.cameraIndexCode)
|
||||
})
|
||||
}, 2000)
|
||||
// if (!videoList.value.length) return false;
|
||||
// timer = setInterval(() => {
|
||||
// postVideoRemainApi({
|
||||
// cameraIndexCode: videoList.value.map((item) => item.cameraIndexCode)
|
||||
// })
|
||||
// }, 2000)
|
||||
}
|
||||
|
||||
const initVideo = () => {
|
||||
clearHlsRefs()
|
||||
nextTick(() => {
|
||||
videoList.value.forEach(async (item, index) => {
|
||||
const video = document.getElementById(`monitorVideo${index}`)
|
||||
const hls = new Hls({
|
||||
const videoElement = document.getElementById(`monitorVideo${index}`)
|
||||
if(item.hlsUrl.startsWith("ws")){
|
||||
const player = mpegtsjs.createPlayer({
|
||||
url: item.hlsUrl,
|
||||
type: 'flv',
|
||||
isLive: true,
|
||||
hasAudio: false
|
||||
})
|
||||
player.attachMediaElement(videoElement)
|
||||
player.load()
|
||||
player.play()
|
||||
|
||||
// 错误处理和重连机制
|
||||
player.on('error', (err) => {
|
||||
console.error('播放器错误:', err)
|
||||
// 3 秒后尝试重新加载
|
||||
setTimeout(() => {
|
||||
player.load()
|
||||
player.play()
|
||||
}, 3000)
|
||||
})
|
||||
hlsRefs.push(player)
|
||||
}else{
|
||||
const player = new Hls({
|
||||
maxBufferLength: 10, // 最大缓冲长度(秒)
|
||||
maxMaxBufferLength: 15, // 缓冲区长度的上限
|
||||
maxBufferSize: 30 * 1000 * 1000 // 最大缓冲大小(字节)
|
||||
})
|
||||
hls.loadSource(item.hlsUrl)
|
||||
hls.attachMedia(video)
|
||||
hls.on(Hls.Events.MANIFEST_PARSED, () => {
|
||||
video.play()
|
||||
player.loadSource(item.hlsUrl)
|
||||
player.attachMedia(videoElement)
|
||||
player.on(Hls.Events.MANIFEST_PARSED, () => {
|
||||
videoElement.play()
|
||||
})
|
||||
hlsRefs.push(hls)
|
||||
player.on(Hls.Events.ERROR, (event, data) => {
|
||||
// console.error('HLS 播放器遇到错误:', data);
|
||||
// hls.startLoad();
|
||||
// initVideo()
|
||||
// 根据错误类型进行处理
|
||||
if (data.fatal) {
|
||||
switch (data.type) {
|
||||
case Hls.ErrorTypes.NETWORK_ERROR:
|
||||
console.log('网络错误,尝试重新加载');
|
||||
player.startLoad();
|
||||
break;
|
||||
case Hls.ErrorTypes.MEDIA_ERROR:
|
||||
console.log('媒体错误,尝试修复');
|
||||
player.recoverMediaError();
|
||||
break;
|
||||
default:
|
||||
console.log('无法恢复的错误,销毁播放器');
|
||||
// hls.destroy();
|
||||
break;
|
||||
}
|
||||
}
|
||||
})
|
||||
hlsRefs.push(player)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -343,6 +394,26 @@
|
||||
videoLog.value = 2
|
||||
cameraIndexCode.value = code
|
||||
setTimeout(() => {
|
||||
if(url.startsWith('ws')){
|
||||
hlsRef = mpegtsjs.createPlayer({
|
||||
url: url,
|
||||
type: 'flv',
|
||||
isLive: true,
|
||||
hasAudio: false
|
||||
})
|
||||
hlsRef.attachMediaElement(videoRef.value)
|
||||
hlsRef.load()
|
||||
hlsRef.play()
|
||||
// 错误处理和重连机制
|
||||
hlsRef.on('error', (err) => {
|
||||
console.error('播放器错误:', err)
|
||||
// 3 秒后尝试重新加载
|
||||
setTimeout(() => {
|
||||
hlsRef.load()
|
||||
hlsRef.play()
|
||||
}, 3000)
|
||||
})
|
||||
}else{
|
||||
hlsRef = new Hls({
|
||||
maxBufferLength: 10, // 最大缓冲长度(秒)
|
||||
maxMaxBufferLength: 15, // 缓冲区长度的上限
|
||||
@@ -353,6 +424,8 @@
|
||||
hlsRef.on(Hls.Events.MANIFEST_PARSED, () => {
|
||||
videoRef.value.play()
|
||||
})
|
||||
}
|
||||
|
||||
if (type == 100) initVideo()
|
||||
}, 1000)
|
||||
}
|
||||
@@ -457,18 +530,41 @@
|
||||
// postVideoRemain()
|
||||
nextTick(() => {
|
||||
videoList.value.forEach(async (x, index) => {
|
||||
const video = document.getElementById(`monitorVideo${index}`)
|
||||
const hls = new Hls({
|
||||
const videoElement = document.getElementById(`monitorVideo${index}`)
|
||||
if(x.hlsUrl.startsWith('ws')){
|
||||
const player = mpegtsjs.createPlayer({
|
||||
url: x.hlsUrl,
|
||||
type: 'flv',
|
||||
isLive: true,
|
||||
hasAudio: false
|
||||
})
|
||||
player.attachMediaElement(videoElement)
|
||||
player.load()
|
||||
player.play()
|
||||
|
||||
// 错误处理和重连机制
|
||||
player.on('error', (err) => {
|
||||
console.error('播放器错误:', err)
|
||||
// 3 秒后尝试重新加载
|
||||
setTimeout(() => {
|
||||
player.load()
|
||||
player.play()
|
||||
}, 3000)
|
||||
})
|
||||
|
||||
hlsRefs.push(player)
|
||||
}else{
|
||||
const player = new Hls({
|
||||
maxBufferLength: 10, // 最大缓冲长度(秒)
|
||||
maxMaxBufferLength: 15, // 缓冲区长度的上限
|
||||
maxBufferSize: 30 * 1000 * 1000 // 最大缓冲大小(字节)
|
||||
})
|
||||
hls.loadSource(x.hlsUrl)
|
||||
hls.attachMedia(video)
|
||||
hls.on(Hls.Events.MANIFEST_PARSED, () => {
|
||||
video.play()
|
||||
player.loadSource(x.hlsUrl)
|
||||
player.attachMedia(videoElement)
|
||||
player.on(Hls.Events.MANIFEST_PARSED, () => {
|
||||
videoElement.play()
|
||||
})
|
||||
hls.on(Hls.Events.ERROR, (event, data) => {
|
||||
player.on(Hls.Events.ERROR, (event, data) => {
|
||||
// console.error('HLS 播放器遇到错误:', data);
|
||||
// hls.startLoad();
|
||||
// initVideo()
|
||||
@@ -477,22 +573,21 @@
|
||||
switch (data.type) {
|
||||
case Hls.ErrorTypes.NETWORK_ERROR:
|
||||
console.log('网络错误,尝试重新加载');
|
||||
hls.startLoad();
|
||||
player.startLoad();
|
||||
break;
|
||||
case Hls.ErrorTypes.MEDIA_ERROR:
|
||||
console.log('媒体错误,尝试修复');
|
||||
hls.recoverMediaError();
|
||||
player.recoverMediaError();
|
||||
break;
|
||||
default:
|
||||
console.log('无法恢复的错误,销毁播放器');
|
||||
|
||||
// hls.destroy();
|
||||
break;
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
hlsRefs.push(hls)
|
||||
hlsRefs.push(player)
|
||||
}
|
||||
})
|
||||
})
|
||||
} else {
|
||||
@@ -594,11 +689,13 @@
|
||||
position: relative;
|
||||
// left:vw(-15);
|
||||
}
|
||||
|
||||
.tree-item__child-item {
|
||||
.new-title:last-child {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.action {
|
||||
&-box {
|
||||
margin-top: vh(16);
|
||||
@@ -607,37 +704,44 @@
|
||||
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);
|
||||
}
|
||||
|
||||
.item-sc {
|
||||
padding: vw(10);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//背景色设置为透明
|
||||
:deep(.el-input__wrapper) {
|
||||
background-color: rgba(0, 0, 0, 0);
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
//输入框颜色
|
||||
:deep(.el-input__inner) {
|
||||
background-color: rgba(0, 0, 0, 0) !important;
|
||||
@@ -650,17 +754,21 @@
|
||||
display: flex;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
:deep(.el-input__inner) {
|
||||
height: vh(36);
|
||||
font-size: vw(16);
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.left-nav {
|
||||
margin: 0 vw(8);
|
||||
width: vw(250);
|
||||
background: linear-gradient(321deg, #0b2f64 0%, #062b57 91%, rgba(5, 40, 79, 0) 100%);
|
||||
|
||||
.bom-box {
|
||||
margin-top: vh(20);
|
||||
|
||||
.search-box {
|
||||
border-radius: vw(2);
|
||||
height: vh(36);
|
||||
@@ -689,15 +797,18 @@
|
||||
&::-webkit-scrollbar {
|
||||
width: vw(4); /* 滚动条的宽度 */
|
||||
}
|
||||
|
||||
/* 滚动条轨道 */
|
||||
&::-webkit-scrollbar-track {
|
||||
background: 'transparent'; /* 轨道的背景色 */
|
||||
}
|
||||
|
||||
/* 滚动条滑块 */
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: rgba(0, 150, 255, 0.63); /* 滑块的背景色 */
|
||||
border-radius: 5px; /* 滑块的圆角 */
|
||||
}
|
||||
|
||||
.tree-item {
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
@@ -706,20 +817,24 @@
|
||||
&:nth-child(1) {
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
&__node {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
&__icon {
|
||||
margin-left: vw(-8);
|
||||
width: vw(16);
|
||||
height: auto;
|
||||
}
|
||||
|
||||
&__icon-up {
|
||||
@extend .tree-item__icon;
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
&__name {
|
||||
padding: 0 vw(5);
|
||||
display: block;
|
||||
@@ -730,12 +845,14 @@
|
||||
font-style: normal;
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
&__child {
|
||||
position: relative;
|
||||
margin-top: vh(20);
|
||||
// margin-left: vw(20);
|
||||
// border-left: vw(2) solid #37d8fc;
|
||||
}
|
||||
|
||||
&-top__icon {
|
||||
position: absolute;
|
||||
left: vw(-8);
|
||||
@@ -743,6 +860,7 @@
|
||||
width: vw(16);
|
||||
height: vw(16);
|
||||
}
|
||||
|
||||
&-bottom__icon {
|
||||
position: absolute;
|
||||
left: vw(-8);
|
||||
@@ -750,6 +868,7 @@
|
||||
width: vw(16);
|
||||
height: vw(16);
|
||||
}
|
||||
|
||||
&__child-item {
|
||||
padding: vh(0) vw(20) vh(20) vw(10);
|
||||
cursor: pointer;
|
||||
@@ -762,6 +881,7 @@
|
||||
// display: flex;
|
||||
align-items: flex-start;
|
||||
padding-right: 0;
|
||||
|
||||
&:nth-last-of-type(1) {
|
||||
// padding: vh(0) vw(20);
|
||||
// padding-right:0;
|
||||
@@ -771,6 +891,7 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ul {
|
||||
font-weight: 400;
|
||||
font-size: vw(18);
|
||||
@@ -783,15 +904,18 @@
|
||||
&::-webkit-scrollbar {
|
||||
width: vw(4); /* 滚动条的宽度 */
|
||||
}
|
||||
|
||||
/* 滚动条轨道 */
|
||||
&::-webkit-scrollbar-track {
|
||||
background: 'transparent'; /* 轨道的背景色 */
|
||||
}
|
||||
|
||||
/* 滚动条滑块 */
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: rgba(0, 150, 255, 0.63); /* 滑块的背景色 */
|
||||
border-radius: 5px; /* 滑块的圆角 */
|
||||
}
|
||||
|
||||
.li {
|
||||
cursor: pointer;
|
||||
width: vw(250);
|
||||
@@ -802,6 +926,7 @@
|
||||
background: url('/src/assets/images/m-nav-bg-1.png');
|
||||
background-size: 100% 100%;
|
||||
}
|
||||
|
||||
.active {
|
||||
background: url('/src/assets/images/m-nav-bg-2.png');
|
||||
background-size: 100% 100%;
|
||||
@@ -818,6 +943,7 @@
|
||||
box-sizing: border-box;
|
||||
background-image: url('/src/assets/images/log-v-bg.png');
|
||||
background-size: 100% 100%;
|
||||
|
||||
.pagination {
|
||||
padding: vh(10) vw(30);
|
||||
position: absolute;
|
||||
@@ -825,12 +951,14 @@
|
||||
bottom: vh(20);
|
||||
}
|
||||
}
|
||||
|
||||
&-list {
|
||||
gap: vw(8);
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-content: flex-start;
|
||||
}
|
||||
|
||||
&-item {
|
||||
position: relative;
|
||||
// width: vw(686);
|
||||
@@ -839,12 +967,14 @@
|
||||
padding: vh(10) vw(10);
|
||||
background-image: url('/src/assets/images/item-primary.png');
|
||||
background-size: 100% 100%;
|
||||
|
||||
&:hover {
|
||||
.video-item__follow {
|
||||
display: block !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-item__follow {
|
||||
cursor: pointer;
|
||||
display: none;
|
||||
@@ -862,6 +992,7 @@
|
||||
background-image: url('@/assets/images/unfollow.png');
|
||||
background-size: 100% 100%;
|
||||
}
|
||||
|
||||
&-item__unfollow {
|
||||
@extend .video-item__follow;
|
||||
background-image: url('@/assets/images/unfollow.png');
|
||||
@@ -870,6 +1001,7 @@
|
||||
&-item__inner {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
&-item__title {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
@@ -883,24 +1015,29 @@
|
||||
text-overflow: ellipsis;
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
&-item__title--error {
|
||||
@extend .video-item__title;
|
||||
background-color: rgba(226, 27, 27, 0.72);
|
||||
}
|
||||
|
||||
&-item__title--primary {
|
||||
@extend .video-item__title;
|
||||
background-color: rgba(4, 30, 69, 0.72);
|
||||
}
|
||||
|
||||
&-item__video {
|
||||
width: 100%;
|
||||
height: vh(366);
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
&-detail {
|
||||
margin-left: vw(10);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
&-detail__wrapper {
|
||||
position: relative;
|
||||
padding: vh(40) vw(50);
|
||||
@@ -909,6 +1046,7 @@
|
||||
background-image: url('/src/assets/images/one-video-bg.png');
|
||||
background-size: 100% 100%;
|
||||
}
|
||||
|
||||
&-detail__title {
|
||||
position: absolute;
|
||||
left: vw(50);
|
||||
@@ -923,33 +1061,39 @@
|
||||
justify-content: space-between;
|
||||
background: rgba(4, 30, 69, 0.5);
|
||||
}
|
||||
|
||||
&-detail__video {
|
||||
width: 100%;
|
||||
height: vh(820);
|
||||
object-fit: contain;
|
||||
background-color: #000;
|
||||
}
|
||||
|
||||
&-right {
|
||||
margin-left: vw(8);
|
||||
width: vw(440);
|
||||
height: vh(956);
|
||||
background: #082f5a;
|
||||
|
||||
.back-box {
|
||||
cursor: pointer;
|
||||
padding-right: vw(20);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.icon {
|
||||
width: vw(30);
|
||||
height: auto;
|
||||
margin-right: vw(10);
|
||||
}
|
||||
|
||||
& > span {
|
||||
font-weight: bold;
|
||||
font-size: vw(20);
|
||||
color: #ffffff;
|
||||
}
|
||||
}
|
||||
|
||||
.list {
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
@@ -982,9 +1126,11 @@
|
||||
padding: vw(10);
|
||||
background-image: url('@/assets/images/item-primary.png');
|
||||
background-size: 100% 100%;
|
||||
|
||||
& > div {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
&-title {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
@@ -997,14 +1143,17 @@
|
||||
text-overflow: ellipsis;
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
&-title--error {
|
||||
@extend .item-title;
|
||||
background-color: rgba(226, 27, 27, 0.72);
|
||||
}
|
||||
|
||||
&-title--primary {
|
||||
@extend .item-title;
|
||||
background-color: rgba(4, 30, 69, 0.72);
|
||||
}
|
||||
|
||||
&-img {
|
||||
width: 100%;
|
||||
height: vh(164);
|
||||
@@ -1013,6 +1162,7 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.video-live {
|
||||
.video-rt {
|
||||
width: vw(400);
|
||||
@@ -1028,29 +1178,35 @@
|
||||
border-image: linear-gradient(180deg, rgba(0, 150, 255, 1), rgba(0, 90, 153, 0)) 1 1;
|
||||
margin-left: vw(10);
|
||||
padding: vw(20);
|
||||
|
||||
.rt-v-box {
|
||||
overflow-y: auto;
|
||||
/* 滚动条整体样式 */
|
||||
&::-webkit-scrollbar {
|
||||
width: vw(4); /* 滚动条的宽度 */
|
||||
}
|
||||
|
||||
/* 滚动条轨道 */
|
||||
&::-webkit-scrollbar-track {
|
||||
background: 'transparent'; /* 轨道的背景色 */
|
||||
}
|
||||
|
||||
/* 滚动条滑块 */
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: rgba(0, 150, 255, 0.63); /* 滑块的背景色 */
|
||||
border-radius: 5px; /* 滑块的圆角 */
|
||||
}
|
||||
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.title {
|
||||
background-image: url('/src/assets/images/nav-l-t-bg.png');
|
||||
background-size: 100% 100%;
|
||||
margin-bottom: vh(10);
|
||||
position: relative;
|
||||
left: vw(-20);
|
||||
|
||||
span {
|
||||
margin-left: vw(30);
|
||||
font-weight: 800;
|
||||
@@ -1067,6 +1223,7 @@
|
||||
color: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
.rt-video {
|
||||
width: 100%;
|
||||
height: vh(300);
|
||||
@@ -1076,6 +1233,7 @@
|
||||
box-sizing: border-box;
|
||||
margin-bottom: vh(2);
|
||||
position: relative;
|
||||
|
||||
.desc {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
@@ -1094,9 +1252,11 @@
|
||||
text-transform: none;
|
||||
}
|
||||
}
|
||||
|
||||
.v-error-bg {
|
||||
background-image: url('/src/assets/images/v-item-bg-1.png');
|
||||
background-size: 100% 100%;
|
||||
|
||||
.desc {
|
||||
background: rgba(226, 27, 27, 0.5);
|
||||
}
|
||||
|
||||
@@ -151,7 +151,23 @@
|
||||
params.series[0].label.formatter = formatLabel()
|
||||
params.series[0].data = getSeriesData()
|
||||
}
|
||||
setOption(params)
|
||||
const chart = setOption(params);
|
||||
chart.on('legendselectchanged', function (e) {
|
||||
var echartsArr = [];
|
||||
for (let key in e.selected) {
|
||||
if (e.selected[key]) {
|
||||
echartsArr.push(key)
|
||||
}
|
||||
}
|
||||
var echartsNum = 0;
|
||||
props.list.forEach(item => {
|
||||
if(echartsArr.includes(item.name)){
|
||||
echartsNum += parseInt(item.count)
|
||||
}
|
||||
})
|
||||
params.series[0].label.formatter = `{value|${echartsNum}}` + '\n' + `{name|${props.subTitle}}`;
|
||||
setOption(params);
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -531,6 +531,7 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: vh(40);
|
||||
cursor: pointer;
|
||||
&:nth-child(2n + 1) {
|
||||
background: rgba(3, 78, 153, 0.3);
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
<!-- <div class="header-left__camera" @click="videoShow = true">道路监控</div> -->
|
||||
<!-- <div class="header-left__point" @click="videoShow = true">3号点位</div> -->
|
||||
</div>
|
||||
<div class="header-status">{{ item.congestLevelText }} </div>
|
||||
<div class="header-status" v-if="item.congestLevel>0">{{ item.congestLevelText }} </div>
|
||||
</div>
|
||||
<div class="statistics">
|
||||
<div class="statistics-item">
|
||||
|
||||
Reference in New Issue
Block a user