告警列表
This commit is contained in:
parent
8acedde40f
commit
677804fbf6
|
@ -1,28 +1,42 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="home">
|
<div class="alert-container">
|
||||||
<el-row>
|
<el-row class="top-pan">
|
||||||
<el-col :span="16">
|
<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>
|
||||||
|
告警总数:{{ totalItems }}
|
||||||
|
</span>
|
||||||
|
</el-row>
|
||||||
|
<el-row class="table-row">
|
||||||
|
<el-col :span="24">
|
||||||
<div class="table-container">
|
<div class="table-container">
|
||||||
<el-table :data="paginatedData">
|
<el-table :data="tableData" style="width:100%; height: 100%;" header-row-class-name="table-header"
|
||||||
<el-table-column prop="id" label="告警编号" width="140"></el-table-column>
|
:fit="true">
|
||||||
<el-table-column label="告警类型" width="140">
|
<el-table-column prop="id" label="告警编号" min-width="100"></el-table-column>
|
||||||
|
<el-table-column label="告警类型" min-width="150">
|
||||||
<template v-slot="scope">
|
<template v-slot="scope">
|
||||||
<!-- {{ typeMapping[scope.row.types] || codenameTranslate(scope.row.types) }} -->
|
|
||||||
{{ typeMapping[scope.row.types] }}
|
{{ typeMapping[scope.row.types] }}
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="camera.name" label="告警位置" width="180"></el-table-column>
|
<el-table-column prop="camera.name" label="告警位置" min-width="150"></el-table-column>
|
||||||
<el-table-column label="告警时间" width="180">
|
<el-table-column label="告警时间" min-width="200">
|
||||||
<template v-slot="scope">
|
<template v-slot="scope">
|
||||||
{{ formatDateTime(scope.row.started_at) }}
|
{{ formatDateTime(scope.row.ended_at) }}
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="status" label="告警状态" width="140">
|
<el-table-column prop="status" label="告警状态" min-width="100">
|
||||||
<template v-slot="scope">
|
<template v-slot="scope">
|
||||||
<el-tag :type="scope.row.status === 'pending' ? 'warning' : 'success'">{{ statusMapping[scope.row.status] }}</el-tag>
|
<el-tag :type="scope.row.status === 'pending' ? 'warning' : 'success'">{{ statusMapping[scope.row.status]
|
||||||
|
}}</el-tag>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="操作" width="140">
|
<el-table-column label="操作" min-width="100">
|
||||||
<template v-slot="scope">
|
<template v-slot="scope">
|
||||||
<el-button type="text" @click="handleView(scope.row)">查看</el-button>
|
<el-button type="text" @click="handleView(scope.row)">查看</el-button>
|
||||||
</template>
|
</template>
|
||||||
|
@ -30,23 +44,11 @@
|
||||||
</el-table>
|
</el-table>
|
||||||
</div>
|
</div>
|
||||||
<div class="pagination-container">
|
<div class="pagination-container">
|
||||||
<el-pagination background layout="total, sizes, prev, pager, next, jumper" :total="tableData.length"
|
<el-pagination @size-change="handleSizeChange" @current-change="handlePageChange" :current-page="currentPage"
|
||||||
:page-size="pageSize" :current-page.sync="currentPage" @current-change="handlePageChange"
|
:page-size="pageSize" :total="totalItems" layout="total, sizes, prev, pager, next, jumper">
|
||||||
@size-change="handleSizeChange">
|
|
||||||
</el-pagination>
|
</el-pagination>
|
||||||
</div>
|
</div>
|
||||||
</el-col>
|
</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-row>
|
||||||
<el-dialog v-model="dialogVisible" title="告警详情" width="80%">
|
<el-dialog v-model="dialogVisible" title="告警详情" width="80%">
|
||||||
<div>
|
<div>
|
||||||
|
@ -55,7 +57,6 @@
|
||||||
<p>告警编号: {{ selectedRow.id }}</p>
|
<p>告警编号: {{ selectedRow.id }}</p>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="6">
|
<el-col :span="6">
|
||||||
<!-- <p>告警类型: {{ typeMapping[selectedRow.types] || codenameTranslate(selectedRow.types) }}</p> -->
|
|
||||||
<p>告警类型: {{ typeMapping[selectedRow.types] }}</p>
|
<p>告警类型: {{ typeMapping[selectedRow.types] }}</p>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="6">
|
<el-col :span="6">
|
||||||
|
@ -67,7 +68,8 @@
|
||||||
</el-row>
|
</el-row>
|
||||||
<el-row>
|
<el-row>
|
||||||
<el-col :span="6">
|
<el-col :span="6">
|
||||||
<p>告警状态: <el-tag :type="selectedRow.status === 'pending' ? 'warning' : 'success'">{{ statusMapping[selectedRow.status]
|
<p>告警状态: <el-tag :type="selectedRow.status === 'pending' ? 'warning' : 'success'">{{
|
||||||
|
statusMapping[selectedRow.status]
|
||||||
}}</el-tag></p>
|
}}</el-tag></p>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="6">
|
<el-col :span="6">
|
||||||
|
@ -100,222 +102,180 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
import * as echarts from 'echarts';
|
import { ref, reactive, onMounted } from 'vue';
|
||||||
import Statistics from '@/components/Statistics.vue';
|
import Statistics from '@/components/Statistics.vue';
|
||||||
import AlertChart from '@/components/AlertChart.vue';
|
import AlertChart from '@/components/AlertChart.vue';
|
||||||
import { login, getEvents, initCodeNameMap, codenameTranslate, getAlgorithms } from '@/utils/superbox.js';
|
import { BoxApi } from '@/utils/boxApi.ts'; // 导入 BoxApi
|
||||||
|
|
||||||
const username = 'turingvideo';
|
// 创建 BoxApi 实例
|
||||||
const password = '1234qwer!';
|
const boxApi = new BoxApi();
|
||||||
|
|
||||||
export default {
|
// 响应式数据
|
||||||
name: 'AlertManagement',
|
const tableData = ref([]);
|
||||||
components: {
|
const dialogVisible = ref(false);
|
||||||
Statistics,
|
const selectedRow = ref({});
|
||||||
AlertChart,
|
const mediums = ref([]);
|
||||||
},
|
const duration = ref('');
|
||||||
data() {
|
const typeMapping = reactive({});
|
||||||
return {
|
const statusMapping = {
|
||||||
tableData: [],
|
'pending': '待处理',
|
||||||
dialogVisible: false,
|
'closed': '已处理'
|
||||||
selectedRow: {},
|
};
|
||||||
mediums: {},
|
const currentPage = ref(1);
|
||||||
duration: '',
|
const pageSize = ref(20);
|
||||||
typeMapping: {
|
const totalItems = ref(0);
|
||||||
// 'abnormal:525': '黑屏检测',
|
const token = ref(null);
|
||||||
// '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,
|
|
||||||
|
|
||||||
}
|
// 获取类型映射
|
||||||
},
|
const fetchTypeMapping = async (token) => {
|
||||||
async created() {
|
try {
|
||||||
try {
|
const algorithms = await boxApi.getAlgorithms(token); // 使用 BoxApi 的 getAlgorithms 方法
|
||||||
const token = await login(username, password);
|
const additionalMappings = [{ code_name: 'minizawu:532', name: '杂物堆积' }];
|
||||||
// await initCodeNameMap(token);
|
algorithms.forEach((algorithm) => {
|
||||||
this.tableData = await getEvents(token);
|
typeMapping[algorithm.code_name] = algorithm.name;
|
||||||
this.typeMapping = await this.fetchTypeMapping(token);
|
});
|
||||||
console.log(this.tableData);
|
} catch (error) {
|
||||||
} catch (error) {
|
console.error("Error fetching algorithms:", 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}`;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 获取告警数据
|
||||||
|
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;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching events data:", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 查看告警详情
|
||||||
|
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>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.home {
|
.alert-container {
|
||||||
padding: 40px;
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
background-color: #f5f7fa;
|
background-color: #f5f7fa;
|
||||||
}
|
}
|
||||||
|
|
||||||
.breadcrumb {
|
.top-pan {
|
||||||
margin-bottom: 30px;
|
padding: 0px;
|
||||||
}
|
margin: 0px;
|
||||||
|
|
||||||
.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;
|
display: flex;
|
||||||
flex-direction: column;
|
gap: 10px;
|
||||||
gap: 20px;
|
background-color: #fff;
|
||||||
padding: 10px 10px 400px 10px;
|
overflow: auto;
|
||||||
background-color: #f5f5f5;
|
|
||||||
height: 600px;
|
|
||||||
overflow-y: auto;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.panel-section {
|
.panel-section {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
padding: 20px;
|
|
||||||
box-shadow: 0 20px 8px rgba(0, 0, 0, 0.1);
|
box-shadow: 0 20px 8px rgba(0, 0, 0, 0.1);
|
||||||
|
border-radius: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.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;
|
border-radius: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 弹窗> 显示左右等分*/
|
.table-container {
|
||||||
|
width: 100%;
|
||||||
|
height: 380px;
|
||||||
|
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 {
|
.event-media {
|
||||||
/* display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 10px;
|
|
||||||
margin-top: 20px; */
|
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
/* 添加这个属性来左右等分 */
|
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.media-container {
|
.media-container {
|
||||||
flex: 0 0 48%;
|
flex: 0 0 48%;
|
||||||
/* 确保每块区域占据一行的48% */
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
/* 使内容在区域内居中 */
|
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
/* 添加底部间距,使多个元素之间有间隔 */
|
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
/* 确保padding和border不会影响大小 */
|
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
/* 添加内边距,使内容不靠近边框 */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.video-item video,
|
.video-item video,
|
||||||
.image-item img {
|
.image-item img {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
/* 保证视频和图片在容器内等宽 */
|
|
||||||
height: auto;
|
height: auto;
|
||||||
/* 保证视频和图片比例正常 */
|
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
/* 圆润效果 */
|
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||||
/* 阴影效果 */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.video-item p,
|
.video-item p,
|
||||||
|
@ -328,7 +288,12 @@ export default {
|
||||||
.pagination-container {
|
.pagination-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
margin-top: 20px;
|
background-color: #e8e9e4;
|
||||||
margin-right: 150px;
|
}
|
||||||
|
|
||||||
|
::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>
|
||||||
|
|
Loading…
Reference in New Issue