初版页面备份-列表加图表

This commit is contained in:
龚皓 2024-10-16 14:45:46 +08:00
parent c8f02faaa7
commit 8184d3ca94
1 changed files with 353 additions and 10 deletions

View File

@ -1,12 +1,355 @@
<template> <template>
<div> <div class="alert-container">
nihao <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>
</template>
<script lang="ts">
<!-- 告警关联图片 -->
<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> <script setup>
<style scoped> 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': '已处理'
};
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 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);
}
};
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;
}
.top-pan {
padding: 0px;
margin: 0px;
display: flex;
gap: 10px;
background-color: #fff;
overflow: auto;
}
.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>
</style>