|
|
@@ -1,4 +1,4 @@
|
|
|
-<template>
|
|
|
+ <!-- <template>
|
|
|
<div class="screen-container">
|
|
|
<div class="top-section">
|
|
|
<div class="system-title">风门视频图像综合解析系统</div>
|
|
|
@@ -14,7 +14,6 @@
|
|
|
</select>
|
|
|
<button class="btn btn-primary" @click="refreshVideo">刷新视频</button>
|
|
|
|
|
|
- <!-- 当前时间 -->
|
|
|
<div class="time-box">当前时间:{{ currentTime }}</div>
|
|
|
</div>
|
|
|
|
|
|
@@ -22,10 +21,8 @@
|
|
|
<div class="video-item">
|
|
|
<div class="video-title">前门 [{{ currentDoorName }}]</div>
|
|
|
<div class="video-box">
|
|
|
- <!-- 视频标签:所有属性齐全,兼容所有浏览器 -->
|
|
|
<video class="video-bg" autoplay loop muted playsinline webkit-playsinline controls>
|
|
|
- <source src="@/assets/images/wind-video/video/v1.mp4" type="video/mp4">
|
|
|
- <!-- 兜底提示:视频不支持时显示 -->
|
|
|
+ <source src="@/assets/images/wind-video/video/v1.mp4" type="video/mp4" />
|
|
|
您的浏览器不支持视频播放
|
|
|
</video>
|
|
|
</div>
|
|
|
@@ -34,8 +31,7 @@
|
|
|
<div class="video-title">后门 [{{ currentDoorName }}]</div>
|
|
|
<div class="video-box">
|
|
|
<video class="video-bg" autoplay loop muted playsinline webkit-playsinline controls>
|
|
|
- <source src="@/assets/images/wind-video/video/v2.mp4" type="video/mp4">
|
|
|
- <!-- 兜底提示:视频不支持时显示 -->
|
|
|
+ <source src="@/assets/images/wind-video/video/v2.mp4" type="video/mp4" />
|
|
|
您的浏览器不支持视频播放
|
|
|
</video>
|
|
|
</div>
|
|
|
@@ -83,8 +79,7 @@
|
|
|
</div>
|
|
|
</div>
|
|
|
<div class="alarm-scroll" ref="alarmScroll">
|
|
|
- <div class="alarm-item" v-for="(item, idx) in alarmList" :key="idx"
|
|
|
- :class="[item.level, item.new ? 'new' : '']">
|
|
|
+ <div class="alarm-item" v-for="(item, idx) in alarmList" :key="idx" :class="[item.level, item.new ? 'new' : '']">
|
|
|
<span class="alarm-time">{{ item.time }}</span>
|
|
|
<span class="alarm-door">{{ getDoorName(item.doorId) }}</span>
|
|
|
<span class="alarm-type">{{ item.type }}</span>
|
|
|
@@ -100,9 +95,9 @@
|
|
|
<div class="search-panel">
|
|
|
<div class="search-item">
|
|
|
<label>时间范围:</label>
|
|
|
- <input type="datetime-local" v-model="searchForm.startTime" class="search-input">
|
|
|
+ <input type="datetime-local" v-model="searchForm.startTime" class="search-input" />
|
|
|
<span class="split">-</span>
|
|
|
- <input type="datetime-local" v-model="searchForm.endTime" class="search-input">
|
|
|
+ <input type="datetime-local" v-model="searchForm.endTime" class="search-input" />
|
|
|
<button class="btn btn-sm btn-default" @click="selectToday">今日</button>
|
|
|
<button class="btn btn-sm btn-default" @click="select7d">近7天</button>
|
|
|
</div>
|
|
|
@@ -131,7 +126,7 @@
|
|
|
<div class="content-wrap">
|
|
|
<div class="img-list">
|
|
|
<div class="img-item" v-for="(item, idx) in currentImgList" :key="idx" @click="openImgModal(item)">
|
|
|
- <img :src="item.imgUrl" class="img-thumbnail">
|
|
|
+ <img :src="item.imgUrl" class="img-thumbnail" />
|
|
|
<div class="img-desc">
|
|
|
<p class="desc-line1">{{ getDoorName(item.doorId) }}</p>
|
|
|
<p class="desc-line2">
|
|
|
@@ -147,8 +142,7 @@
|
|
|
<div class="tab-nav">
|
|
|
<div class="tab-item" :class="{ active: activeTab === 'car' }" @click="activeTab = 'car'">车辆通行记录</div>
|
|
|
<div class="tab-item" :class="{ active: activeTab === 'person' }" @click="activeTab = 'person'">人员通行记录</div>
|
|
|
- <div class="tab-item" :class="{ active: activeTab === 'unclose' }" @click="activeTab = 'unclose'">风门未关严记录
|
|
|
- </div>
|
|
|
+ <div class="tab-item" :class="{ active: activeTab === 'unclose' }" @click="activeTab = 'unclose'">风门未关严记录 </div>
|
|
|
</div>
|
|
|
|
|
|
<div class="data-list">
|
|
|
@@ -206,7 +200,7 @@
|
|
|
<span>图像详情</span>
|
|
|
<button class="close-btn" @click="closeImgModal">×</button>
|
|
|
</div>
|
|
|
- <img :src="currentModalImg" class="modal-img">
|
|
|
+ <img :src="currentModalImg" class="modal-img" />
|
|
|
<div class="modal-info">
|
|
|
<p>{{ modalInfo }}</p>
|
|
|
</div>
|
|
|
@@ -214,27 +208,26 @@
|
|
|
</div>
|
|
|
</div>
|
|
|
</template>
|
|
|
-
|
|
|
<script setup lang="ts">
|
|
|
-import { ref, onMounted, onUnmounted } from 'vue'
|
|
|
-import { doorList,statusData,alarmTypeList,createRandomAlarm,currentImgList,carList,personList,uncloseList } from './windDoorVideo.data'
|
|
|
-
|
|
|
-const selectDoor = ref('all')
|
|
|
-const currentDoorId = ref(1)
|
|
|
-const currentDoorName = ref('五盘区402辅运大巷风门')
|
|
|
-const videoUrl1 = ref('https://picsum.photos/800/600?random=1')
|
|
|
-const videoUrl2 = ref('https://picsum.photos/800/600?random=2')
|
|
|
-const showImgModal = ref(false)
|
|
|
-const currentModalImg = ref('')
|
|
|
-const modalInfo = ref('')
|
|
|
-const activeTab = ref('car')
|
|
|
-const currentTime = ref('')
|
|
|
+import { ref, onMounted, onUnmounted } from 'vue';
|
|
|
+import { doorList, statusData, alarmTypeList, createRandomAlarm, currentImgList, carList, personList, uncloseList } from './windDoorVideo.data';
|
|
|
+
|
|
|
+const selectDoor = ref('all');
|
|
|
+const currentDoorId = ref(1);
|
|
|
+const currentDoorName = ref('五盘区402辅运大巷风门');
|
|
|
+const videoUrl1 = ref('https://picsum.photos/800/600?random=1');
|
|
|
+const videoUrl2 = ref('https://picsum.photos/800/600?random=2');
|
|
|
+const showImgModal = ref(false);
|
|
|
+const currentModalImg = ref('');
|
|
|
+const modalInfo = ref('');
|
|
|
+const activeTab = ref('car');
|
|
|
+const currentTime = ref('');
|
|
|
// https获取监测数据
|
|
|
let timeTimer: null | NodeJS.Timeout = null;
|
|
|
-let addAlarmTimer: null | NodeJS.Timeout = null;// 新增:定时添加告警定时器
|
|
|
+let addAlarmTimer: null | NodeJS.Timeout = null; // 新增:定时添加告警定时器
|
|
|
let alarmTimer: null | NodeJS.Timeout = null; // 原有滚动定时器
|
|
|
-const alarmScroll = ref<any>(null)
|
|
|
-const searchForm = ref({ startTime: '', endTime: '', alarmType: 'all', doorId: 'all' })
|
|
|
+const alarmScroll = ref<any>(null);
|
|
|
+const searchForm = ref({ startTime: '', endTime: '', alarmType: 'all', doorId: 'all' });
|
|
|
const alarmList = ref<any[]>([
|
|
|
{ time: '16:30:22', doorId: 2, type: '防夹人预警', desc: '危险区域有人闯入,已触发PLC停止', level: 'red' },
|
|
|
{ time: '16:28:15', doorId: 4, type: '风门未关严', desc: '门板闭合缝隙15mm,超标10mm', level: 'yellow' },
|
|
|
@@ -245,18 +238,16 @@ const alarmList = ref<any[]>([
|
|
|
{ time: '16:05:12', doorId: 2, type: '防夹人预警', desc: '风门闭合区检测到人员肢体,紧急止停', level: 'red' },
|
|
|
{ time: '16:00:58', doorId: 1, type: '风门未关严', desc: '门板闭合缝隙8mm,接近阈值', level: 'gray' },
|
|
|
{ time: '15:55:33', doorId: 5, type: '异物卡堵', desc: '风门轨道有杂物', level: 'yellow' },
|
|
|
- { time: '15:50:20', doorId: 3, type: '违规停留', desc: '禁停区有人员逗留,已语音提醒', level: 'gray' }
|
|
|
-])
|
|
|
-
|
|
|
-
|
|
|
+ { time: '15:50:20', doorId: 3, type: '违规停留', desc: '禁停区有人员逗留,已语音提醒', level: 'gray' },
|
|
|
+]);
|
|
|
|
|
|
// 新增:定时添加新告警到顶部
|
|
|
const startAddAlarm = () => {
|
|
|
// 每5-10秒随机生成1条(可自行调整时间)
|
|
|
- const randomTime = 5000 + Math.floor(Math.random() * 5000)
|
|
|
+ const randomTime = 5000 + Math.floor(Math.random() * 5000);
|
|
|
addAlarmTimer = setInterval(() => {
|
|
|
- const newAlarm = createRandomAlarm()
|
|
|
- alarmList.value.unshift(newAlarm) // 插入到列表顶部,实现“新告警优先展示”
|
|
|
+ const newAlarm = createRandomAlarm();
|
|
|
+ alarmList.value.unshift(newAlarm); // 插入到列表顶部,实现“新告警优先展示”
|
|
|
newAlarm.new = true;
|
|
|
// 1.8 秒后清除动画
|
|
|
setTimeout(() => {
|
|
|
@@ -264,104 +255,112 @@ const startAddAlarm = () => {
|
|
|
}, 1800);
|
|
|
// 限制列表长度,避免数据过多(保留20条,可调整)
|
|
|
if (alarmList.value.length > 20) {
|
|
|
- alarmList.value.pop()
|
|
|
+ alarmList.value.pop();
|
|
|
}
|
|
|
- }, randomTime)
|
|
|
-}
|
|
|
+ }, randomTime);
|
|
|
+};
|
|
|
const getDoorName = (id) => {
|
|
|
- const d = doorList.find(i => i.id === id)
|
|
|
- return d ? d.name : '未知风门'
|
|
|
-}
|
|
|
+ const d = doorList.find((i) => i.id === id);
|
|
|
+ return d ? d.name : '未知风门';
|
|
|
+};
|
|
|
const switchDoor = () => {
|
|
|
if (selectDoor.value !== 'all') {
|
|
|
- currentDoorId.value = +selectDoor.value
|
|
|
- currentDoorName.value = getDoorName(currentDoorId.value)
|
|
|
- videoUrl1.value = `https://picsum.photos/800/600?random=${Math.random()}`
|
|
|
- videoUrl2.value = `https://picsum.photos/800/600?random=${Math.random()}`
|
|
|
+ currentDoorId.value = +selectDoor.value;
|
|
|
+ currentDoorName.value = getDoorName(currentDoorId.value);
|
|
|
+ videoUrl1.value = `https://picsum.photos/800/600?random=${Math.random()}`;
|
|
|
+ videoUrl2.value = `https://picsum.photos/800/600?random=${Math.random()}`;
|
|
|
}
|
|
|
-}
|
|
|
+};
|
|
|
const refreshVideo = () => {
|
|
|
- videoUrl1.value = `https://picsum.photos/800/600?random=${Math.random()}`
|
|
|
- videoUrl2.value = `https://picsum.photos/800/600?random=${Math.random()}`
|
|
|
-}
|
|
|
-const clearAlarm = () => { alarmList.value = [] }
|
|
|
+ videoUrl1.value = `https://picsum.photos/800/600?random=${Math.random()}`;
|
|
|
+ videoUrl2.value = `https://picsum.photos/800/600?random=${Math.random()}`;
|
|
|
+};
|
|
|
+const clearAlarm = () => {
|
|
|
+ alarmList.value = [];
|
|
|
+};
|
|
|
const formatDate = (d) => {
|
|
|
- const y = d.getFullYear()
|
|
|
- const m = String(d.getMonth() + 1).padStart(2, '0')
|
|
|
- const dt = String(d.getDate()).padStart(2, '0')
|
|
|
- const h = String(d.getHours()).padStart(2, '0')
|
|
|
- const mi = String(d.getMinutes()).padStart(2, '0')
|
|
|
- return `${y}-${m}-${dt}T${h}:${mi}`
|
|
|
-}
|
|
|
+ const y = d.getFullYear();
|
|
|
+ const m = String(d.getMonth() + 1).padStart(2, '0');
|
|
|
+ const dt = String(d.getDate()).padStart(2, '0');
|
|
|
+ const h = String(d.getHours()).padStart(2, '0');
|
|
|
+ const mi = String(d.getMinutes()).padStart(2, '0');
|
|
|
+ return `${y}-${m}-${dt}T${h}:${mi}`;
|
|
|
+};
|
|
|
|
|
|
const selectToday = () => {
|
|
|
- const now = new Date()
|
|
|
- const st = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 0, 0, 0)
|
|
|
- searchForm.value.startTime = formatDate(st)
|
|
|
- searchForm.value.endTime = formatDate(now)
|
|
|
-}
|
|
|
+ const now = new Date();
|
|
|
+ const st = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 0, 0, 0);
|
|
|
+ searchForm.value.startTime = formatDate(st);
|
|
|
+ searchForm.value.endTime = formatDate(now);
|
|
|
+};
|
|
|
|
|
|
const select7d = () => {
|
|
|
- const now = new Date()
|
|
|
- const st = new Date(now.getTime() - 7 * 86400000)
|
|
|
- searchForm.value.startTime = formatDate(st)
|
|
|
- searchForm.value.endTime = formatDate(now)
|
|
|
-}
|
|
|
-
|
|
|
-const handleSearch = () => { alert('检索成功') }
|
|
|
-const resetSearch = () => { searchForm.value = { startTime: '', endTime: '', alarmType: 'all', doorId: 'all' } }
|
|
|
+ const now = new Date();
|
|
|
+ const st = new Date(now.getTime() - 7 * 86400000);
|
|
|
+ searchForm.value.startTime = formatDate(st);
|
|
|
+ searchForm.value.endTime = formatDate(now);
|
|
|
+};
|
|
|
+
|
|
|
+const handleSearch = () => {
|
|
|
+ alert('检索成功');
|
|
|
+};
|
|
|
+const resetSearch = () => {
|
|
|
+ searchForm.value = { startTime: '', endTime: '', alarmType: 'all', doorId: 'all' };
|
|
|
+};
|
|
|
|
|
|
const openImgModal = (item) => {
|
|
|
- showImgModal.value = true
|
|
|
- currentModalImg.value = item.imgUrl
|
|
|
- let info = ''
|
|
|
- if (item.type === '车辆通行') info = `${getDoorName(item.doorId)} | 车辆通行/${item.license}`
|
|
|
- if (item.type === '人员通行') info = `${getDoorName(item.doorId)} | 人员通行/${item.personName}`
|
|
|
- if (item.type === '风门未关严') info = `${getDoorName(item.doorId)} | 缝隙${item.gap}mm`
|
|
|
- modalInfo.value = info
|
|
|
-}
|
|
|
-const closeImgModal = () => { showImgModal.value = false }
|
|
|
+ showImgModal.value = true;
|
|
|
+ currentModalImg.value = item.imgUrl;
|
|
|
+ let info = '';
|
|
|
+ if (item.type === '车辆通行') info = `${getDoorName(item.doorId)} | 车辆通行/${item.license}`;
|
|
|
+ if (item.type === '人员通行') info = `${getDoorName(item.doorId)} | 人员通行/${item.personName}`;
|
|
|
+ if (item.type === '风门未关严') info = `${getDoorName(item.doorId)} | 缝隙${item.gap}mm`;
|
|
|
+ modalInfo.value = info;
|
|
|
+};
|
|
|
+const closeImgModal = () => {
|
|
|
+ showImgModal.value = false;
|
|
|
+};
|
|
|
|
|
|
const updateTime = () => {
|
|
|
- const d = new Date()
|
|
|
- const y = d.getFullYear()
|
|
|
- const m = String(d.getMonth() + 1).padStart(2, '0')
|
|
|
- const dt = String(d.getDate()).padStart(2, '0')
|
|
|
- const h = String(d.getHours()).padStart(2, '0')
|
|
|
- const mi = String(d.getMinutes()).padStart(2, '0')
|
|
|
- const se = String(d.getSeconds()).padStart(2, '0')
|
|
|
- currentTime.value = `${y}-${m}-${dt} ${h}:${mi}:${se}`
|
|
|
-}
|
|
|
+ const d = new Date();
|
|
|
+ const y = d.getFullYear();
|
|
|
+ const m = String(d.getMonth() + 1).padStart(2, '0');
|
|
|
+ const dt = String(d.getDate()).padStart(2, '0');
|
|
|
+ const h = String(d.getHours()).padStart(2, '0');
|
|
|
+ const mi = String(d.getMinutes()).padStart(2, '0');
|
|
|
+ const se = String(d.getSeconds()).padStart(2, '0');
|
|
|
+ currentTime.value = `${y}-${m}-${dt} ${h}:${mi}:${se}`;
|
|
|
+};
|
|
|
// 恢复告警滚动(优化流畅度)
|
|
|
const startAlarmScroll = () => {
|
|
|
- if (!alarmScroll.value) return
|
|
|
+ if (!alarmScroll.value) return;
|
|
|
alarmTimer = setInterval(() => {
|
|
|
- const scrollDom = alarmScroll.value
|
|
|
- const firstItem = scrollDom.firstElementChild
|
|
|
+ const scrollDom = alarmScroll.value;
|
|
|
+ const firstItem = scrollDom.firstElementChild;
|
|
|
if (firstItem) {
|
|
|
// 平滑滚动到下一条,贴合工业界面体验
|
|
|
- scrollDom.scrollTop += 28
|
|
|
+ scrollDom.scrollTop += 28;
|
|
|
// 滚动到底部时重置
|
|
|
if (scrollDom.scrollTop >= scrollDom.scrollHeight - scrollDom.clientHeight) {
|
|
|
- scrollDom.scrollTop = 0
|
|
|
+ scrollDom.scrollTop = 0;
|
|
|
}
|
|
|
}
|
|
|
- }, 1500) // 滚动速度(数值越小越快,可调整)
|
|
|
-}
|
|
|
+ }, 1500); // 滚动速度(数值越小越快,可调整)
|
|
|
+};
|
|
|
|
|
|
onMounted(() => {
|
|
|
- selectToday()
|
|
|
- updateTime()
|
|
|
- timeTimer = setInterval(updateTime, 1000)
|
|
|
- startAlarmScroll() // 恢复原有滚动
|
|
|
- startAddAlarm() // 新增:启动定时添加新告警
|
|
|
-})
|
|
|
+ selectToday();
|
|
|
+ updateTime();
|
|
|
+ timeTimer = setInterval(updateTime, 1000);
|
|
|
+ startAlarmScroll(); // 恢复原有滚动
|
|
|
+ startAddAlarm(); // 新增:启动定时添加新告警
|
|
|
+});
|
|
|
|
|
|
onUnmounted(() => {
|
|
|
- clearInterval(timeTimer)
|
|
|
- if (alarmTimer) clearInterval(alarmTimer) // 销毁滚动定时器
|
|
|
- if (addAlarmTimer) clearInterval(addAlarmTimer) // 销毁新增告警定时器
|
|
|
-})
|
|
|
+ clearInterval(timeTimer);
|
|
|
+ if (alarmTimer) clearInterval(alarmTimer); // 销毁滚动定时器
|
|
|
+ if (addAlarmTimer) clearInterval(addAlarmTimer); // 销毁新增告警定时器
|
|
|
+});
|
|
|
</script>
|
|
|
|
|
|
<style lang="less" scoped>
|
|
|
@@ -371,7 +370,7 @@ onUnmounted(() => {
|
|
|
height: 100%;
|
|
|
background: #0a102b;
|
|
|
color: #e0efff;
|
|
|
- font-family: "Microsoft YaHei", sans-serif;
|
|
|
+ font-family: 'Microsoft YaHei', sans-serif;
|
|
|
/* 关键修复:禁止页面整体滚动,内部区域自己滚动 */
|
|
|
overflow: hidden !important;
|
|
|
padding: 8px;
|
|
|
@@ -1035,4 +1034,4 @@ onUnmounted(() => {
|
|
|
z-index: 10;
|
|
|
/* 新告警浮在最上层 */
|
|
|
}
|
|
|
-</style>
|
|
|
+</style> -->
|