本地告警页面V0

This commit is contained in:
龚皓
2024-09-04 16:23:57 +08:00
commit c9ba2ad556
29 changed files with 3831 additions and 0 deletions

View File

@@ -0,0 +1,334 @@
<template>
<div class="home">
<el-row>
<el-col :span="16">
<div class="table-container">
<el-table :data="paginatedData">
<el-table-column prop="id" label="告警编号" width="140"></el-table-column>
<el-table-column label="告警类型" width="140">
<template v-slot="scope">
<!-- {{ typeMapping[scope.row.types] || codenameTranslate(scope.row.types) }} -->
{{ typeMapping[scope.row.types] }}
</template>
</el-table-column>
<el-table-column prop="camera.name" label="告警位置" width="180"></el-table-column>
<el-table-column label="告警时间" width="180">
<template v-slot="scope">
{{ formatDateTime(scope.row.started_at) }}
</template>
</el-table-column>
<el-table-column prop="status" label="告警状态" width="140">
<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="操作" width="140">
<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 background layout="total, sizes, prev, pager, next, jumper" :total="tableData.length"
:page-size="pageSize" :current-page.sync="currentPage" @current-change="handlePageChange"
@size-change="handleSizeChange">
</el-pagination>
</div>
</el-col>
<el-col :span="8">
<div class="right-panel">
<div class="panel-section">
<statistics />
</div>
<div class="panel-section">
<alertChart />
</div>
</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] || codenameTranslate(selectedRow.types) }}</p> -->
<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>
<span slot="footer" class="dialog-footer">
<div class=""></div>
</span>
</el-dialog>
</div>
</template>
<script>
import * as echarts from 'echarts';
import Statistics from '@/components/Statistics.vue';
import AlertChart from '@/components/AlertChart.vue';
import { login, getEvents, initCodeNameMap, codenameTranslate, getAlgorithms } from '@/utils/superbox.js';
const username = 'turingvideo';
const password = '1234qwer!';
export default {
name: 'AlertManagement',
components: {
Statistics,
AlertChart,
},
data() {
return {
tableData: [],
dialogVisible: false,
selectedRow: {},
mediums: {},
duration: '',
typeMapping: {
// 'abnormal:525': '黑屏检测',
// 'car_blocking:512': '车辆拥塞',
// 'car_blocking:514': '违规停车',
// 'escalator_status:518': '扶梯运行状态',
// 'gathering:520': '人员密集',
// 'gathering:521': '保洁点名',
// 'intrude:513': '入侵检测',
// 'long_term_door_status:526': '长期门状态检测',
// 'lying_down:527': '人员倒地',
// 'minizawu:531': '杂物堆积',
// 'minizawu:532': '杂物堆积',
// 'personnel_stay:535': '人员逗留',
// 'vacant:524': '人员离岗',
// 'zawu:516': '饮料垃圾检测',
// 'zawu:517': '垃圾桶满溢',
// 'zawu:519': '违规放置',
// 'zawu:523': '绿化带垃圾检测',
},
statusMapping: {
'pending': '待处理',
'closed': '已处理'
},
currentPage: 1,
pageSize: 20,
}
},
async created() {
try {
const token = await login(username, password);
// await initCodeNameMap(token);
this.tableData = await getEvents(token);
this.typeMapping = await this.fetchTypeMapping(token);
console.log(this.tableData);
} catch (error) {
console.error("Error fetching events:", error);
}
},
computed: {
paginatedData() {
const start = (this.currentPage - 1) * this.pageSize;
const end = start + this.pageSize;
return this.tableData.slice(start, end);
}
},
methods: {
async fetchTypeMapping(token) {
let algorithms = await getAlgorithms(token);
const additionalMappings = [
{ code_name: 'minizawu:532', name: '杂物堆积' },
];
algorithms = algorithms.concat(additionalMappings);
let mapping = {};
algorithms.forEach((algorithm) => {
mapping[algorithm.code_name] = algorithm.name;
});
// console.log("mapping: ", mapping);
return mapping;
},
handleView(row) {
this.selectedRow = row;
this.duration = this.calculateDuration(row.started_at, row.ended_at);
row.formatted_started_at = this.formatDateTime(row.started_at);
// console.log("duration: ", row.started_at);
// console.log("duration: ", row.ended_at);
// console.log("duration: ", this.duration);
this.dialogVisible = true;
this.mediums = row.mediums || {};
if (Array.isArray(row.mediums)) {
row.mediums.forEach(medium => {
// console.log("medium: ", medium.file);
// console.log("medium: ", medium.id);
});
} else {
// console.log("No mediums available");
}
},
handlePageChange(page) {
this.currentPage = page;
},
handleSizeChange(size) {
this.pageSize = size;
},
calculateDuration(started_at, ended_at) {
const start = new Date(started_at);
const end = new Date(ended_at);
const duration = end - start;
const minutes = Math.floor(duration / 60000);
const seconds = ((duration % 60000) / 1000).toFixed(0);
return `${minutes}${(seconds < 10 ? '0' : '')}${seconds}`;
},
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}`;
}
}
};
</script>
<style scoped>
.home {
padding: 40px;
background-color: #f5f7fa;
}
.breadcrumb {
margin-bottom: 30px;
}
.top-scroll {
width: 100%;
overflow-x: auto;
}
.top-scroll-content {
/* 高度滚动条显示出来默认值 */
height: 1px;
}
.table-container {
width: 80%;
max-height: 1000px;
overflow-x: auto;
padding-left: 7%;
}
.right-panel {
display: flex;
flex-direction: column;
gap: 20px;
padding: 10px 10px 400px 10px;
background-color: #f5f5f5;
height: 600px;
overflow-y: auto;
}
.panel-section {
flex: 1;
background-color: #fff;
padding: 20px;
box-shadow: 0 20px 8px rgba(0, 0, 0, 0.1);
border-radius: 8px;
}
/* 弹窗> 显示左右等分*/
.event-media {
/* display: flex;
flex-direction: column;
gap: 10px;
margin-top: 20px; */
display: flex;
justify-content: space-between;
/* 添加这个属性来左右等分 */
flex-wrap: wrap;
}
.media-container {
flex: 0 0 48%;
/* 确保每块区域占据一行的48% */
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
/* 使内容在区域内居中 */
margin-bottom: 20px;
/* 添加底部间距,使多个元素之间有间隔 */
box-sizing: border-box;
/* 确保padding和border不会影响大小 */
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;
margin-top: 20px;
margin-right: 150px;
}
</style>