告警大屏完成
This commit is contained in:
@@ -215,6 +215,7 @@ onBeforeUnmount(() => {
|
||||
box-sizing: border-box;
|
||||
border-right: 1px solid #1E2E4A;
|
||||
padding-right: 10px;
|
||||
padding-bottom: 10vh;
|
||||
}
|
||||
|
||||
.search-input {
|
||||
|
||||
@@ -1,355 +1,365 @@
|
||||
<template>
|
||||
<div class="alert-container">
|
||||
<el-row class="top-pan">
|
||||
<el-col :sm="24" :md="12" class="panel-section">
|
||||
<statistics />
|
||||
</el-col>
|
||||
<el-col :sm="24" :md="12" class="panel-section">
|
||||
<alertChart />
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row class="middle-row">
|
||||
<span>
|
||||
告警总数:{{ displayTotalItems }}
|
||||
</span>
|
||||
</el-row>
|
||||
<el-row class="table-row">
|
||||
<el-col :span="24">
|
||||
<div class="table-container">
|
||||
<el-table :data="tableData" style="width:100%; height: 100%;" header-row-class-name="table-header" :fit="true">
|
||||
<el-table-column prop="id" label="告警编号" min-width="100"></el-table-column>
|
||||
<el-table-column label="告警类型" min-width="150">
|
||||
<template v-slot="scope">
|
||||
{{ typeMapping[scope.row.types] }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="camera.name" label="告警位置" min-width="150"></el-table-column>
|
||||
<el-table-column label="告警时间" min-width="200">
|
||||
<template v-slot="scope">
|
||||
{{ formatDateTime(scope.row.ended_at) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="status" label="告警状态" min-width="100">
|
||||
<template v-slot="scope">
|
||||
<el-tag :type="scope.row.status === 'pending' ? 'warning' : 'success'">{{ statusMapping[scope.row.status]
|
||||
}}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" min-width="100">
|
||||
<template v-slot="scope">
|
||||
<el-button type="text" @click="handleView(scope.row)">查看</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
<div class="pagination-container">
|
||||
<el-pagination @size-change="handleSizeChange" @current-change="handlePageChange" :current-page="currentPage"
|
||||
:page-size="pageSize" :total="totalItems" layout="total, sizes, prev, pager, next, jumper">
|
||||
</el-pagination>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-dialog v-model="dialogVisible" title="告警详情" width="80%">
|
||||
<div>
|
||||
<el-row>
|
||||
<el-col :span="6">
|
||||
<p>告警编号: {{ selectedRow.id }}</p>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<p>告警类型: {{ typeMapping[selectedRow.types] }}</p>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<p>告警位置: {{ selectedRow.camera.name }}</p>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<p>告警时间: {{ selectedRow.formatted_started_at }}</p>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="6">
|
||||
<p>告警状态: <el-tag :type="selectedRow.status === 'pending' ? 'warning' : 'success'">{{
|
||||
statusMapping[selectedRow.status]
|
||||
}}</el-tag></p>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<p>摄像头编号: {{ selectedRow.camera_id }}</p>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<p>持续时间: {{ duration }}</p>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<p>备注: {{ selectedRow.note }}</p>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<!-- <div class="event-media">
|
||||
<div v-for="medium in selectedRow.mediums" :key="medium.id" class="media-container">
|
||||
<div v-if="medium.name === 'video'" class="media-item video-item">
|
||||
<p>告警关联视频</p>
|
||||
<video :src="medium.file" controls></video>
|
||||
</div>
|
||||
<div v-if="medium.name === 'snapshot'" class="media-item image-item">
|
||||
<p>告警关联图片</p>
|
||||
<el-image :src="medium.file" fit="contain"></el-image>
|
||||
</div>
|
||||
</div>
|
||||
</div> -->
|
||||
<div class="event-media" :class="{ 'center-media': mediums.length === 1 }">
|
||||
<!-- 告警关联视频 -->
|
||||
<div v-if="hasVideo" class="media-container">
|
||||
<p>告警关联视频</p>
|
||||
<video :src="videoFile" controls></video>
|
||||
</div>
|
||||
|
||||
<!-- 告警关联图片 -->
|
||||
<div v-if="hasSnapshot" class="media-container">
|
||||
<p>告警关联图片</p>
|
||||
<el-image :src="snapshotFile" fit="contain"></el-image>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<div class=""></div>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted, computed } from 'vue';
|
||||
import Statistics from '@/components/Statistics.vue';
|
||||
import AlertChart from '@/components/AlertChart.vue';
|
||||
import { BoxApi } from '@/utils/boxApi.ts'; // 导入 BoxApi
|
||||
|
||||
// 创建 BoxApi 实例
|
||||
const boxApi = new BoxApi();
|
||||
|
||||
// 响应式数据
|
||||
const tableData = ref([]);
|
||||
const dialogVisible = ref(false);
|
||||
const selectedRow = ref({});
|
||||
const mediums = ref([]);
|
||||
const duration = ref('');
|
||||
const typeMapping = reactive({});
|
||||
const statusMapping = {
|
||||
'pending': '待处理',
|
||||
'closed': '已处理'
|
||||
<el-tabs v-model="activeTab" class="tab-div">
|
||||
<el-tab-pane label="所有" name="all"></el-tab-pane>
|
||||
<el-tab-pane label="今天" name="today"></el-tab-pane>
|
||||
<el-tab-pane label="7天" name="week"></el-tab-pane>
|
||||
<el-tab-pane label="30天" name="month"></el-tab-pane>
|
||||
</el-tabs>
|
||||
|
||||
<el-row class="top-row">
|
||||
<el-col :span="8">
|
||||
<div>
|
||||
<el-row>
|
||||
<!-- 左侧占据一整行 -->
|
||||
<el-col :sm="24" :md="8">
|
||||
<CameraAll />
|
||||
</el-col>
|
||||
<!-- 右侧分为两行 -->
|
||||
<el-col :sm="24" :md="16">
|
||||
<el-row>
|
||||
<el-col :sm="24" :md="24">
|
||||
通道总数:
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :sm="24" :md="24" class="inner-count-text">
|
||||
{{ cameraCount }}
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div>
|
||||
<el-row>
|
||||
<!-- 左侧占据一整行 -->
|
||||
<el-col :sm="24" :md="8">
|
||||
<CameraOnline />
|
||||
</el-col>
|
||||
<!-- 右侧分为两行 -->
|
||||
<el-col :sm="24" :md="16">
|
||||
<el-row>
|
||||
<el-col :sm="24" :md="24">
|
||||
在线:
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :sm="24" :md="24" class="inner-count-text">
|
||||
{{ cameraOnlineCount }}
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div>
|
||||
<el-row>
|
||||
<!-- 左侧占据一整行 -->
|
||||
<el-col :sm="24" :md="8">
|
||||
<CameraOffline />
|
||||
</el-col>
|
||||
<!-- 右侧分为两行 -->
|
||||
<el-col :sm="24" :md="16">
|
||||
<el-row>
|
||||
<el-col :sm="24" :md="24">
|
||||
离线:
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :sm="24" :md="24" class="inner-count-text">
|
||||
{{ cameraOfflineCount }}
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row class="bottom-row">
|
||||
<el-col :span="8">
|
||||
<div>
|
||||
<el-row>
|
||||
<!-- 左侧占据一整行 -->
|
||||
<el-col :sm="24" :md="8">
|
||||
<EventAll />
|
||||
</el-col>
|
||||
<!-- 右侧分为两行 -->
|
||||
<el-col :sm="24" :md="16">
|
||||
<el-row>
|
||||
<el-col :sm="24" :md="24">
|
||||
事件总数:
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :sm="24" :md="24" class="inner-count-text">
|
||||
{{ eventCount }}
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div>
|
||||
<el-row>
|
||||
<!-- 左侧占据一整行 -->
|
||||
<el-col :sm="24" :md="8">
|
||||
<EventClosed />
|
||||
</el-col>
|
||||
<!-- 右侧分为两行 -->
|
||||
<el-col :sm="24" :md="16">
|
||||
<el-row>
|
||||
<el-col :sm="24" :md="24">
|
||||
已处理:
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :sm="24" :md="24" class="inner-count-text">
|
||||
{{ closedEventCount }}
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div>
|
||||
<el-row>
|
||||
<!-- 左侧占据一整行 -->
|
||||
<el-col :sm="24" :md="8">
|
||||
<EventPending />
|
||||
</el-col>
|
||||
<!-- 右侧分为两行 -->
|
||||
<el-col :sm="24" :md="16">
|
||||
<el-row>
|
||||
<el-col :sm="24" :md="24">
|
||||
未处理:
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :sm="24" :md="24" class="inner-count-text">
|
||||
{{ pendintingEventCount }}
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, onBeforeUnmount, watch } from 'vue';
|
||||
import * as echarts from 'echarts';
|
||||
import { BoxApi } from '@/utils/boxApi.ts';
|
||||
import CameraAll from '@/icons/CameraAll.vue';
|
||||
import CameraOnline from '@/icons/CameraOnline.vue';
|
||||
import CameraOffline from '@/icons/CameraOffline.vue';
|
||||
import EventAll from '@/icons/EventAll.vue';
|
||||
import EventClosed from '@/icons/EventClosed.vue';
|
||||
import EventPending from '@/icons/EventPending.vue';
|
||||
|
||||
const apiInstance = new BoxApi();
|
||||
const cameraCount = ref(0);
|
||||
const cameraOfflineCount = ref(0);
|
||||
const cameraOnlineCount = ref(0);
|
||||
const eventCount = ref(0);
|
||||
const pendintingEventCount = ref(0);
|
||||
const closedEventCount = ref(0);
|
||||
|
||||
const activeTab = ref('all');
|
||||
|
||||
const getTodayData = () => {
|
||||
const today = new Date();
|
||||
const startOfToday = new Date(today.getFullYear(), today.getMonth(), today.getDate());
|
||||
const endOfToday = new Date(today.getFullYear(), today.getMonth(), today.getDate() + 1);
|
||||
|
||||
return {
|
||||
timeAfter: formatDateTime(startOfToday),
|
||||
timeBefore: formatDateTime(endOfToday),
|
||||
};
|
||||
const currentPage = ref(1);
|
||||
const pageSize = ref(20);
|
||||
const token = ref(null);
|
||||
const totalItems = ref(0);
|
||||
const displayTotalItems = ref(0); // 用于展示的数字
|
||||
|
||||
// 计算属性:根据 mediums 动态判断是否存在视频和图片
|
||||
const hasVideo = computed(() => mediums.value.some(medium => medium.name === 'video'));
|
||||
const hasSnapshot = computed(() => mediums.value.some(medium => medium.name === 'snapshot'));
|
||||
|
||||
// 获取视频和图片的文件
|
||||
const videoFile = computed(() => {
|
||||
const video = mediums.value.find(medium => medium.name === 'video');
|
||||
return video ? video.file : '';
|
||||
});
|
||||
|
||||
const snapshotFile = computed(() => {
|
||||
const snapshot = mediums.value.find(medium => medium.name === 'snapshot');
|
||||
return snapshot ? snapshot.file : '';
|
||||
});
|
||||
|
||||
// 获取类型映射
|
||||
const fetchTypeMapping = async (token) => {
|
||||
try {
|
||||
const algorithms = await boxApi.getAlgorithms(token); // 使用 BoxApi 的 getAlgorithms 方法
|
||||
const additionalMappings = [{ code_name: 'minizawu:532', name: '杂物堆积' }];
|
||||
algorithms.forEach((algorithm) => {
|
||||
typeMapping[algorithm.code_name] = algorithm.name;
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Error fetching algorithms:", error);
|
||||
};
|
||||
|
||||
const getWeekData = () => {
|
||||
const today = new Date();
|
||||
const startOfWeek = new Date(today.getFullYear(), today.getMonth(), today.getDate() - 6);
|
||||
const endOfToday = new Date(today.getFullYear(), today.getMonth(), today.getDate() + 1);
|
||||
|
||||
return {
|
||||
timeAfter: formatDateTime(startOfWeek),
|
||||
timeBefore: formatDateTime(endOfToday),
|
||||
};
|
||||
};
|
||||
|
||||
const getMonthData = () => {
|
||||
const today = new Date();
|
||||
const startOfMonth = new Date(today.getFullYear(), today.getMonth(), today.getDate() - 29);
|
||||
const endOfToday = new Date(today.getFullYear(), today.getMonth(), today.getDate() + 1);
|
||||
|
||||
return {
|
||||
timeAfter: formatDateTime(startOfMonth),
|
||||
timeBefore: formatDateTime(endOfToday),
|
||||
};
|
||||
};
|
||||
|
||||
const formatDateTime = (datetime) => {
|
||||
const date = new Date(datetime);
|
||||
const year = date.getFullYear();
|
||||
const month = (date.getMonth() + 1).toString().padStart(2, '0');
|
||||
const day = date.getDate().toString().padStart(2, '0');
|
||||
const hours = date.getHours().toString().padStart(2, '0');
|
||||
const minutes = date.getMinutes().toString().padStart(2, '0');
|
||||
const seconds = date.getSeconds().toString().padStart(2, '0');
|
||||
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
|
||||
};
|
||||
|
||||
const fetchCameras = async () => {
|
||||
try {
|
||||
const token = localStorage.getItem('alertToken');
|
||||
const limit = 20;
|
||||
let offset = 0;
|
||||
let allCameras = [];
|
||||
|
||||
const firstResponse = await apiInstance.getCameras(limit, offset, token);
|
||||
cameraCount.value = firstResponse.count;
|
||||
console.log("总数》》》》》》》》》》》》》", cameraCount.value)
|
||||
allCameras = firstResponse.results;
|
||||
|
||||
|
||||
const total = cameraCount.value;
|
||||
while (offset + limit < total) {
|
||||
offset += limit;
|
||||
const response = await apiInstance.getCameras(limit, offset, token);
|
||||
allCameras = allCameras.concat(response.results);
|
||||
}
|
||||
};
|
||||
|
||||
// 获取告警数据
|
||||
const fetchEvents = async () => {
|
||||
try {
|
||||
const { tableData: data, totalItems: total } = await boxApi.getEvents(token.value, pageSize.value, currentPage.value); // 使用 BoxApi 的 getEvents 方法
|
||||
tableData.value = data;
|
||||
totalItems.value = total;
|
||||
animateNumberChange(total);
|
||||
} catch (error) {
|
||||
console.error("Error fetching events data:", error);
|
||||
}
|
||||
};
|
||||
|
||||
const animateNumberChange = (newTotal) => {
|
||||
const stepTime = 50; // 每次递增的时间间隔
|
||||
// const stepCount = Math.abs(newTotal - displayTotalItems.value);
|
||||
const stepCount = Math.abs(newTotal - displayTotalItems.value) / 20;
|
||||
|
||||
const incrementStep = Math.ceil((newTotal - displayTotalItems.value) / stepCount); // 每次递增的值
|
||||
|
||||
const step = () => {
|
||||
if (displayTotalItems.value < newTotal) {
|
||||
displayTotalItems.value = Math.min(displayTotalItems.value + incrementStep, newTotal);
|
||||
requestAnimationFrame(step);
|
||||
} else if (displayTotalItems.value > newTotal) {
|
||||
displayTotalItems.value = Math.max(displayTotalItems.value - incrementStep, newTotal);
|
||||
requestAnimationFrame(step);
|
||||
|
||||
allCameras.forEach((camera) => {
|
||||
if (camera.status === 'online') {
|
||||
cameraOnlineCount.value++;
|
||||
} else if (camera.status === 'offline') {
|
||||
cameraOfflineCount.value++;
|
||||
} else {
|
||||
exceptionCount.value++;
|
||||
}
|
||||
};
|
||||
|
||||
step();
|
||||
};
|
||||
|
||||
// 查看告警详情
|
||||
const handleView = (row) => {
|
||||
selectedRow.value = row;
|
||||
duration.value = calculateDuration(row.started_at, row.ended_at);
|
||||
row.formatted_started_at = formatDateTime(row.started_at);
|
||||
dialogVisible.value = true;
|
||||
mediums.value = row.mediums || [];
|
||||
};
|
||||
|
||||
// 分页处理
|
||||
const handlePageChange = (page) => {
|
||||
currentPage.value = page;
|
||||
fetchEvents();
|
||||
};
|
||||
|
||||
// 页大小处理
|
||||
const handleSizeChange = (size) => {
|
||||
pageSize.value = size;
|
||||
fetchEvents();
|
||||
};
|
||||
|
||||
// 计算持续时间
|
||||
const calculateDuration = (started_at, ended_at) => {
|
||||
const start = new Date(started_at);
|
||||
const end = new Date(ended_at);
|
||||
const durationMs = end - start;
|
||||
const minutes = Math.floor(durationMs / 60000);
|
||||
const seconds = ((durationMs % 60000) / 1000).toFixed(0);
|
||||
return `${minutes}分${seconds < 10 ? '0' : ''}${seconds}秒`;
|
||||
};
|
||||
|
||||
// 格式化日期时间
|
||||
const formatDateTime = (datetime) => {
|
||||
const date = new Date(datetime);
|
||||
const year = date.getFullYear();
|
||||
const month = (date.getMonth() + 1).toString().padStart(2, '0');
|
||||
const day = date.getDate().toString().padStart(2, '0');
|
||||
const hours = date.getHours().toString().padStart(2, '0');
|
||||
const minutes = date.getMinutes().toString().padStart(2, '0');
|
||||
const seconds = date.getSeconds().toString().padStart(2, '0');
|
||||
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
|
||||
};
|
||||
|
||||
// 页面加载后执行
|
||||
onMounted(async () => {
|
||||
token.value = localStorage.getItem('alertToken');
|
||||
await fetchTypeMapping(token.value);
|
||||
await fetchEvents();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.alert-container {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
background-color: #f5f7fa;
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error fetching cameras:', error);
|
||||
}
|
||||
|
||||
.top-pan {
|
||||
padding: 0px;
|
||||
margin: 0px;
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
background-color: #fff;
|
||||
overflow: auto;
|
||||
};
|
||||
|
||||
|
||||
// const fetchEvents = async (timeAfter = null, timeBefore = null) => {
|
||||
// try {
|
||||
// const token = localStorage.getItem('alertToken');
|
||||
|
||||
// const totalResponse = await apiInstance.getEventsByParams(token, 1, 1, timeBefore, timeAfter);
|
||||
// eventCount.value = totalResponse.count;
|
||||
|
||||
// const pendingResponse = await apiInstance.getEventsByParams(token, 1, 1, timeBefore, timeAfter, null, null, 'pending');
|
||||
// pendintingEventCount.value = pendingResponse.count;
|
||||
|
||||
// const closedResponse = await apiInstance.getEventsByParams(token, 1, 1, timeBefore, timeAfter, null, null, 'closed');
|
||||
// closedEventCount.value = closedResponse.count;
|
||||
|
||||
// } catch (error) {
|
||||
// console.error('获取事件数据失败:', error);
|
||||
// }
|
||||
// };
|
||||
|
||||
|
||||
const fetchEvents = async () => {
|
||||
try {
|
||||
const token = localStorage.getItem('alertToken');
|
||||
let timeRange = { timeAfter: null, timeBefore: null };
|
||||
|
||||
// 根据 activeTab 设置时间范围
|
||||
if (activeTab.value === 'today') {
|
||||
timeRange = getTodayData();
|
||||
} else if (activeTab.value === 'week') {
|
||||
timeRange = getWeekData();
|
||||
} else if (activeTab.value === 'month') {
|
||||
timeRange = getMonthData();
|
||||
}
|
||||
|
||||
// 获取告警总数
|
||||
const totalResponse = await apiInstance.getEventsByParams(token, 1, 1, timeRange.timeBefore, timeRange.timeAfter);
|
||||
eventCount.value = totalResponse.count;
|
||||
|
||||
// 获取状态为 pending 的告警数量
|
||||
const pendingResponse = await apiInstance.getEventsByParams(token, 1, 1, timeRange.timeBefore, timeRange.timeAfter, null, null, 'pending');
|
||||
pendintingEventCount.value = pendingResponse.count;
|
||||
|
||||
// 获取状态为 closed 的告警数量
|
||||
const closedResponse = await apiInstance.getEventsByParams(token, 1, 1, timeRange.timeBefore, timeRange.timeAfter, null, null, 'closed');
|
||||
closedEventCount.value = closedResponse.count;
|
||||
|
||||
} catch (error) {
|
||||
console.error('获取事件数据失败:', error);
|
||||
}
|
||||
|
||||
.panel-section {
|
||||
flex: 1;
|
||||
background-color: #fff;
|
||||
box-shadow: 0 20px 8px rgba(0, 0, 0, 0.1);
|
||||
border-radius: 15px;
|
||||
transform: scale(0.8);
|
||||
transform-origin: center center;
|
||||
}
|
||||
|
||||
.middle-row {
|
||||
min-height: 5vw;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-content: center;
|
||||
font-size: 30px;
|
||||
font-weight: bold;
|
||||
background: linear-gradient(to bottom left, rgb(150, 151, 243), rgb(215, 214, 250));
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.table-container {
|
||||
width: 100%;
|
||||
height: 650px;
|
||||
min-height: 60%;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.table-header {
|
||||
background-color: #f7f8fa;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
::v-deep .el-table th.el-table__cell {
|
||||
background-color: #000;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.event-media {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.center-media {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.media-container {
|
||||
flex: 0 0 48%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 20px;
|
||||
box-sizing: border-box;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.video-item video,
|
||||
.image-item img {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.video-item p,
|
||||
.image-item p {
|
||||
margin-bottom: 8px;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.pagination-container {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
background-color: #e8e9e4;
|
||||
}
|
||||
|
||||
::v-deep .pagination-container .el-pagination__total,
|
||||
::v-deep .pagination-container .el-pagination__goto,
|
||||
::v-deep .pagination-container .el-pagination__classifier {
|
||||
color: #000;
|
||||
}
|
||||
</style>
|
||||
|
||||
};
|
||||
|
||||
watch(activeTab, (newTab) => {
|
||||
fetchEvents();
|
||||
});
|
||||
|
||||
|
||||
|
||||
onMounted(() => {
|
||||
// getTodayData();
|
||||
// getWeekData();
|
||||
// getMonthData();
|
||||
fetchCameras();
|
||||
fetchEvents();
|
||||
|
||||
|
||||
});
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.top-row,
|
||||
.bottom-row {
|
||||
background-color: #001529;
|
||||
color: aliceblue;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.inner-count-text {
|
||||
color: rgb(91, 224, 241);
|
||||
}
|
||||
|
||||
.tab-div{
|
||||
background-color: #001529;
|
||||
}
|
||||
|
||||
::v-deep .el-tabs__item {
|
||||
color: #fff;
|
||||
font-size: 13px;
|
||||
padding: 0;
|
||||
margin-left: 1vh;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
::v-deep .el-tabs__item.is-active {
|
||||
color: #2ea0ec;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.el-tabs__active-bar {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
|
||||
::v-deep .el-tabs__nav-wrap::after {
|
||||
/* width: 15vw; */
|
||||
position: static !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
<dv-decoration-11 class="custom-decoration" style="width:25vw;height:70px;">
|
||||
<div color-green font-700 bg="~ dark/0">
|
||||
<dv-decoration7 style="width:20vw;height:30px;">
|
||||
<div color-white font-300>
|
||||
本地告警大屏
|
||||
<div color-white font-400 >
|
||||
告警数据面板
|
||||
</div>
|
||||
</dv-decoration7>
|
||||
</div>
|
||||
@@ -57,32 +57,43 @@
|
||||
<dv-border-box-13 title="告警数据概览(数据计算数字)" class="section top-left">
|
||||
<LeftTop />
|
||||
</dv-border-box-13>
|
||||
<dv-border-box-13 title="点位告警数量(不同点位的数量)" class="section middle-left">点位告警数量(不同点位的数量)</dv-border-box-13>
|
||||
<dv-border-box-13 title="今日告警列表(告警详情)" class="section bottom-left ">今日告警列表(告警详情)</dv-border-box-13>
|
||||
<dv-border-box-13 title="点位告警数量(不同点位的数量)" class="section middle-left">不同点位告警的数量
|
||||
<LeftMiddle/>
|
||||
</dv-border-box-13>
|
||||
<!-- <dv-border-box-13 title="今日告警列表(告警详情)" class="section bottom-left ">今日告警列表(告警详情)
|
||||
|
||||
</dv-border-box-13> -->
|
||||
</div>
|
||||
|
||||
<!-- 中部区域 -->
|
||||
<div class="center-section">
|
||||
<dv-border-box8 class="center-top">
|
||||
<dv-border-box8 class="center-top-header">警戒画面</dv-border-box8>
|
||||
<CenterTop/>
|
||||
<!-- <dv-border-box8 class="center-top-header">警戒画面</dv-border-box8>
|
||||
<div class="center-top-grids">
|
||||
<div class="grid-item">栅格左上</div>
|
||||
<div class="grid-item">栅格右上</div>
|
||||
<div class="grid-item">栅格左下</div>
|
||||
<div class="grid-item">栅格右下</div>
|
||||
</div>
|
||||
</div> -->
|
||||
</dv-border-box8>
|
||||
<div class="center-bottom">
|
||||
<!-- <dv-border-box-13 class="center-bottom-left">中下左</dv-border-box-13> -->
|
||||
<dv-border-box-13 class="center-bottom-right">警戒点位列表</dv-border-box-13>
|
||||
<dv-border-box-13 class="center-bottom-right">告警数量分布情况
|
||||
<CenterBottom/>
|
||||
</dv-border-box-13>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 右侧区域 -->
|
||||
<div class="right-section">
|
||||
<dv-border-box-13 class="section top-right corner-style">告警类型概览</dv-border-box-13>
|
||||
<dv-border-box-13 class="section middle-right corner-style">告警数量分布</dv-border-box-13>
|
||||
<dv-border-box-13 class="section bottom-right corner-style">告警种类划分</dv-border-box-13>
|
||||
<dv-border-box-13 class="section top-right corner-style">时间段告警总数分布
|
||||
<RightTop/>
|
||||
</dv-border-box-13>
|
||||
<!-- <dv-border-box-13 class="section middle-right corner-style">告警数量分布</dv-border-box-13> -->
|
||||
<dv-border-box-13 class="section bottom-right corner-style">告警种类划分
|
||||
<LeftBottom/>
|
||||
</dv-border-box-13>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -95,6 +106,11 @@ import { ref, onMounted, onBeforeUnmount, nextTick, computed } from 'vue';
|
||||
import { BoxApi } from '@/utils/boxApi.ts';
|
||||
import { VideoPlay, VideoPause, VideoCameraFilled } from '@element-plus/icons-vue';
|
||||
import LeftTop from '@/components/Max/LeftTop.vue';
|
||||
import LeftBottom from '@/components/Max/LeftBottom.vue';
|
||||
import LeftMiddle from '@/components/Max/LeftMiddle.vue';
|
||||
import RightTop from '@/components/Max/RightTop.vue';
|
||||
import CenterBottom from '@/components/Max/CenterBottom.vue';
|
||||
import CenterTop from '@/components/Max/CenterTop.vue';
|
||||
// import '/src/assets/viewListStyle.css'
|
||||
|
||||
</script>
|
||||
@@ -106,38 +122,41 @@ import LeftTop from '@/components/Max/LeftTop.vue';
|
||||
margin: 0;
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
padding: 4vh 10vw 10vh 1vw;
|
||||
padding: 4vh 10vw 10vh 7vw;
|
||||
/* background-color: rgb(121, 184, 243); */
|
||||
background-color: #001529;
|
||||
/* background-image: url('/bg05.png'); */
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background: radial-gradient(circle, rgb(24, 64, 197), rgb(0, 7, 60));
|
||||
/* background: radial-gradient(circle, rgb(24, 64, 197), rgb(0, 7, 60)); */
|
||||
position: relative;
|
||||
color: black;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* .custom-decoration{
|
||||
color:white;
|
||||
} */
|
||||
.custom-decoration{
|
||||
color: #70e5fa;;
|
||||
font-weight: bold;
|
||||
font-size: 25px;
|
||||
}
|
||||
|
||||
|
||||
.background-overlay {
|
||||
/* .background-overlay {
|
||||
position: absolute;
|
||||
/* 绝对定位 */
|
||||
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
/* background-image: url('/bg01.png'); */
|
||||
background-image: url('/bg01.png');
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
opacity: 0.8;
|
||||
z-index: 0;
|
||||
|
||||
}
|
||||
} */
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
@@ -206,11 +225,11 @@ import LeftTop from '@/components/Max/LeftTop.vue';
|
||||
|
||||
}
|
||||
|
||||
.top-left,
|
||||
.middle-left,
|
||||
|
||||
|
||||
.bottom-left,
|
||||
.top-right,
|
||||
.middle-right,
|
||||
/* .middle-right, */
|
||||
.bottom-right {
|
||||
color: white;
|
||||
text-align: center;
|
||||
@@ -225,13 +244,39 @@ import LeftTop from '@/components/Max/LeftTop.vue';
|
||||
position: relative;
|
||||
padding: 1vh 1vw;
|
||||
display: flex;
|
||||
justify-content: center; /* 水平居中 */
|
||||
align-items: center; /* 垂直居中 */
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-grow: 1;
|
||||
width: 20vw;
|
||||
height: 20vh;
|
||||
}
|
||||
|
||||
.middle-left{
|
||||
color: white;
|
||||
text-align: center;
|
||||
width: 22vw;
|
||||
height: 46vh;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.top-right{
|
||||
width: 22vw;
|
||||
height: 46vh;
|
||||
display: flex;
|
||||
/* padding: 1.5vh; */
|
||||
}
|
||||
|
||||
.bottom-left{
|
||||
/* color: white; */
|
||||
/* position: relative; */
|
||||
padding-left: 0.5vw;
|
||||
display: flex;
|
||||
/* justify-content: center; */
|
||||
/* align-items: center; */
|
||||
/* width: 20vw; */
|
||||
/* height: 20vh; */
|
||||
}
|
||||
|
||||
/* .top-left, .top-right {
|
||||
margin-bottom: 1vh;
|
||||
}
|
||||
@@ -257,17 +302,14 @@ import LeftTop from '@/components/Max/LeftTop.vue';
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.center-top-header {
|
||||
/* .center-top-header {
|
||||
height: 3vh;
|
||||
width: 35vw;
|
||||
text-align: center;
|
||||
line-height: 3vh;
|
||||
margin-bottom: 1vh;
|
||||
/* background-color: rgba(0, 51, 102, 0.8); */
|
||||
border-radius: 3px;
|
||||
/* 圆角 */
|
||||
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.5);
|
||||
/* 添加阴影 */
|
||||
}
|
||||
|
||||
.center-top-grids {
|
||||
@@ -275,16 +317,15 @@ import LeftTop from '@/components/Max/LeftTop.vue';
|
||||
grid-template-columns: 17vw 17vw;
|
||||
gap: 2vh 1vw;
|
||||
}
|
||||
|
||||
.grid-item {
|
||||
width: 17vw;
|
||||
height: 20vh;
|
||||
/* background-color: #777; */
|
||||
background-color: #777;
|
||||
color: white;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
} */
|
||||
|
||||
.center-bottom {
|
||||
display: flex;
|
||||
|
||||
Reference in New Issue
Block a user