You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1067 lines
35 KiB
1067 lines
35 KiB
<template>
|
|
<div class="dashboard" @click="handleDocumentClick">
|
|
<!-- 筛选按钮 -->
|
|
<el-button style="position: fixed; top: 80px; right: 10px;" type="primary" @click.stop="toggleFilters">
|
|
{{ filtersVisible ? '隐藏' : '显示' }}筛选
|
|
</el-button>
|
|
|
|
<!-- 条件筛选区域 -->
|
|
<transition name="slide-fade">
|
|
<div v-if="filtersVisible" ref="filterContainer" class="filters-container"
|
|
@click.stop>
|
|
<el-form
|
|
ref="filterForm"
|
|
:inline="true"
|
|
:model="queryParams"
|
|
:rules="rules"
|
|
class="demo-form-inline"
|
|
>
|
|
<el-form-item>
|
|
<el-radio-group v-model="filterType" @change="handleFilterTypeChange">
|
|
<el-radio label="source">事件源</el-radio>
|
|
<el-radio label="section">桩号范围</el-radio>
|
|
<el-radio label="type">事件类型</el-radio>
|
|
<el-radio label="time">时间范围</el-radio>
|
|
</el-radio-group>
|
|
</el-form-item>
|
|
|
|
<!-- 根据事件源统计条件 -->
|
|
<div v-if="filterType === 'source'">
|
|
<el-form-item label="方向">
|
|
<el-select v-model="queryParams.direction" placeholder="请选择" style="margin-left: 27px;">
|
|
<el-option v-for="item in directions" :key="item.value" :label="item.label" :value="item.value"/>
|
|
</el-select>
|
|
</el-form-item>
|
|
<el-form-item label="时间范围">
|
|
<el-date-picker
|
|
v-model="queryParams.typeDateRange"
|
|
end-placeholder="结束日期"
|
|
format="yyyy-MM-dd"
|
|
range-separator="至"
|
|
start-placeholder="开始日期"
|
|
type="daterange"
|
|
value-format="yyyy-MM-dd"
|
|
/>
|
|
</el-form-item>
|
|
</div>
|
|
|
|
<!-- 路段条件 -->
|
|
<div v-if="filterType === 'section'">
|
|
<el-form-item label="事件源">
|
|
<el-select
|
|
v-model="queryParams.warningSourceArray"
|
|
collapse-tags
|
|
multiple
|
|
placeholder="请选择"
|
|
style="width: 100%;margin-left: 10px;"
|
|
>
|
|
<el-option
|
|
v-for="item in eventSources"
|
|
:key="item.value"
|
|
:label="item.label"
|
|
:value="item.value"
|
|
/>
|
|
</el-select>
|
|
</el-form-item>
|
|
<el-form-item label="方 向">
|
|
<el-select v-model="queryParams.direction" placeholder="请选择" style="margin-left: 23px;">
|
|
<el-option v-for="item in directions" :key="item.value" :label="item.label" :value="item.value"/>
|
|
</el-select>
|
|
</el-form-item>
|
|
<el-form-item label="时间范围">
|
|
<el-date-picker
|
|
v-model="queryParams.typeDateRange"
|
|
end-placeholder="结束日期"
|
|
format="yyyy-MM-dd"
|
|
range-separator="至"
|
|
start-placeholder="开始日期"
|
|
type="daterange"
|
|
value-format="yyyy-MM-dd"
|
|
/>
|
|
</el-form-item>
|
|
</div>
|
|
|
|
<!-- 事件类型条件 -->
|
|
<div v-if="filterType === 'type'">
|
|
<!-- 添加起始桩号和结束桩号 -->
|
|
<el-form-item :label="startStation.label" :rules="rules.stakeMarkArry" prop="stakeMarkArry">
|
|
<span style="color: #000000;font-size: 16px">K</span>
|
|
<el-input v-model="queryParams.stakeMarkArry[0]" style="width: 80px;"/>
|
|
<span style="color: #000000;font-size: 25px">+</span>
|
|
<el-input v-model="queryParams.stakeMarkArry[1]" style="width: 80px;"/>
|
|
</el-form-item>
|
|
<el-form-item :label="endStation.label" :rules="rules.endMark" prop="endMark">
|
|
<span style="color: #000000;font-size: 16px">K</span>
|
|
<el-input v-model="queryParams.endMark[0]" style="width: 80px;"/>
|
|
<span style="color: #000000;font-size: 25px">+</span>
|
|
<el-input v-model="queryParams.endMark[1]" style="width: 80px;"/>
|
|
</el-form-item>
|
|
<el-form-item label="时间范围">
|
|
<el-date-picker
|
|
|
|
v-model="queryParams.typeDateRange"
|
|
end-placeholder="结束日期"
|
|
format="yyyy-MM-dd"
|
|
range-separator="至"
|
|
start-placeholder="开始日期"
|
|
type="daterange"
|
|
value-format="yyyy-MM-dd"
|
|
/>
|
|
</el-form-item>
|
|
<el-form-item label="方 向">
|
|
<el-select v-model="queryParams.direction" placeholder="请选择" style="margin-left: 25px;">
|
|
<el-option v-for="item in directions" :key="item.value" :label="item.label" :value="item.value"/>
|
|
</el-select>
|
|
</el-form-item>
|
|
<el-form-item label="事件源">
|
|
<el-select
|
|
v-model="queryParams.warningSourceArray"
|
|
collapse-tags
|
|
multiple
|
|
placeholder="请选择"
|
|
style="width: 100%;margin-left: 13px;"
|
|
>
|
|
<el-option
|
|
v-for="item in eventSources"
|
|
:key="item.value"
|
|
:label="item.label"
|
|
:value="item.value"
|
|
/>
|
|
</el-select>
|
|
</el-form-item>
|
|
<el-form-item label="事件类型">
|
|
<el-select v-model="queryParams.warningType" placeholder="请选择">
|
|
<el-option v-for="item in warningTypes" :key="item.value" :label="item.label" :value="item.value"/>
|
|
</el-select>
|
|
</el-form-item>
|
|
</div>
|
|
|
|
<div v-if="filterType === 'time'">
|
|
|
|
<el-form-item label="日期">
|
|
</el-form-item>
|
|
<el-select v-model="queryParams.type" class="selectRoad" placeholder="请选择" style="margin-left: 14px;">
|
|
|
|
<el-option v-for="item in typeOptions" :key="item.value" :label="item.label" :value="item.value"/>
|
|
</el-select>
|
|
<!-- 日期选择 -->
|
|
<el-date-picker
|
|
v-model="queryParams.dateTime"
|
|
:picker-options="pickerOptions"
|
|
:type="datePickerType"
|
|
class="selectRoad"
|
|
placeholder="请选择"
|
|
size="mini"
|
|
style="width: 140px "
|
|
@change="handleDateChange"
|
|
/>
|
|
<el-form-item label="方 向">
|
|
<el-select v-model="queryParams.direction" placeholder="请选择" style="margin-left: 22px;">
|
|
<el-option v-for="item in directions" :key="item.value" :label="item.label" :value="item.value"/>
|
|
</el-select>
|
|
</el-form-item>
|
|
<el-form-item label="事件源">
|
|
<el-select
|
|
v-model="queryParams.warningSourceArray"
|
|
collapse-tags
|
|
multiple
|
|
placeholder="请选择"
|
|
style="width: 100%;margin-left: 13px;"
|
|
>
|
|
<el-option
|
|
v-for="item in eventSources"
|
|
:key="item.value"
|
|
:label="item.label"
|
|
:value="item.value"
|
|
/>
|
|
</el-select>
|
|
</el-form-item>
|
|
<el-form-item label="事件类型">
|
|
<el-select v-model="queryParams.warningType" placeholder="请选择">
|
|
<el-option v-for="item in warningTypes" :key="item.value" :label="item.label" :value="item.value"/>
|
|
</el-select>
|
|
</el-form-item>
|
|
</div>
|
|
|
|
<!-- 应用筛选按钮 -->
|
|
<el-form-item>
|
|
<el-button type="primary" @click="submitForm">应用筛选</el-button>
|
|
<el-button @click="clearFilters">清除筛选</el-button>
|
|
</el-form-item>
|
|
</el-form>
|
|
</div>
|
|
</transition>
|
|
<div class="charts">
|
|
<div class="bar-chart">
|
|
<div ref="barChart" style="width: 100%; height: 300px;"></div>
|
|
</div>
|
|
<div class="horizontal-bar-chart">
|
|
<div ref="horizontalBarChart" style="width: 100%; height: 300px;"></div>
|
|
</div>
|
|
</div>
|
|
<el-col style="margin-left: 1%">
|
|
<el-button
|
|
icon="el-icon-download"
|
|
|
|
size="mini"
|
|
type="warning"
|
|
@click="handleExport"
|
|
>导出
|
|
</el-button>
|
|
</el-col>
|
|
<div class="table-container">
|
|
|
|
<el-table :data="eventData">
|
|
<el-table-column align="center" label="事件桩号" prop="stakeMark"/>
|
|
<el-table-column align="center" label="事件源">
|
|
<template slot-scope="scope">
|
|
{{ getEventSourceDescription(scope.row.warningSource) }}
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column align="center" label="事件类型">
|
|
<template slot-scope="scope">
|
|
{{ getEventTypeDescription(scope.row.warningSubclass) }}
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column align="center" label="复核结果">
|
|
<template slot-scope="scope">
|
|
{{ getAuditResultDescription(scope.row.auditFlag) }}
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column align="center" label="发生时间" prop="createTime" width="180">
|
|
<template slot-scope="scope">
|
|
<span>{{ parseTime(scope.row.warningTime) }}</span>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column align="center" label="复核时间" prop="createTime" width="180">
|
|
<template slot-scope="scope">
|
|
<span>{{ parseTime(scope.row.auditTime) }}</span>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column align="center" class-name="small-padding fixed-width" label="操作">
|
|
<template slot-scope="scope">
|
|
<el-button
|
|
icon="el-icon-edit"
|
|
size="mini"
|
|
type="text"
|
|
@click="firstBtnClick(scope.row.id)"
|
|
>查看详情
|
|
</el-button>
|
|
</template>
|
|
</el-table-column>
|
|
</el-table>
|
|
|
|
<pagination
|
|
v-show="total>0"
|
|
:limit.sync="queryParams.pageSize"
|
|
:page.sync="queryParams.pageNum"
|
|
:total="total"
|
|
@pagination="event"
|
|
/>
|
|
|
|
</div>
|
|
<!-- 详情模态框 -->
|
|
<el-dialog
|
|
:visible.sync="dialogVisible"
|
|
title="事件详情"
|
|
width="50%"
|
|
>
|
|
<div class="event-details">
|
|
<div class="detail-item">
|
|
<span class="label">事件源:</span>
|
|
<span class="value"> {{ getEventSourceDescription(currentEvent.warningSource) }}</span>
|
|
</div>
|
|
<div class="detail-item">
|
|
<span class="label">桩号:</span>
|
|
<span class="value">{{ currentEvent.stakeMark }}</span>
|
|
</div>
|
|
<div class="detail-item">
|
|
<span class="label">行驶方向:</span>
|
|
<span class="value">{{ getEventDirection(currentEvent.direction) }}</span>
|
|
</div>
|
|
<div class="detail-item">
|
|
<span class="label">事件类型:</span>
|
|
<span class="value">{{ getEventTypeDescription(currentEvent.warningSubclass) }}</span>
|
|
</div>
|
|
<div class="detail-item">
|
|
<span class="label">高速名称:</span>
|
|
<span class="value">{{ currentEvent.highwayName }}</span>
|
|
</div>
|
|
<div class="detail-item">
|
|
<span class="label">事件状态:</span>
|
|
<span class="value">{{ getWarningStateText(currentEvent.warningState) }}</span>
|
|
</div>
|
|
<div class="detail-item">
|
|
<span class="label">操作员:</span>
|
|
<span class="value">{{ currentEvent.auditor }}</span>
|
|
</div>
|
|
<div class="detail-item">
|
|
<span class="label">发生时间:</span>
|
|
<span class="value">{{ parseTime(currentEvent.warningTime) }}</span>
|
|
</div>
|
|
<div class="detail-item">
|
|
<span class="label">预计结束时间:</span>
|
|
<span class="value">{{ parseTime(currentEvent.estimatedEndTime) }}</span>
|
|
</div>
|
|
<div class="detail-item">
|
|
<span class="label">影响车道:</span>
|
|
<span class="value">
|
|
<el-checkbox-group v-model="laneSelection">
|
|
<el-checkbox :label="0">应急</el-checkbox>
|
|
<el-checkbox :label="1">行1</el-checkbox>
|
|
<el-checkbox :label="2">行2</el-checkbox>
|
|
<el-checkbox :label="3">行3</el-checkbox>
|
|
<el-checkbox :label="4">行4</el-checkbox>
|
|
</el-checkbox-group>
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</el-dialog>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
import axios from '@/utils/request';
|
|
import * as echarts from 'echarts';
|
|
import moment from "moment";
|
|
|
|
export default {
|
|
name: 'Dashboard',
|
|
data() {
|
|
return {
|
|
typeOptions: [
|
|
{
|
|
label: "年",
|
|
value: '1',
|
|
},
|
|
{
|
|
label: "月",
|
|
value: '2',
|
|
},
|
|
|
|
],
|
|
pickerOptions: {
|
|
disabledDate(time) {
|
|
return time.getTime() > Date.now();
|
|
},
|
|
},
|
|
// 定义起始桩号和结束桩号的配置
|
|
startStation: {label: "起始桩号:"},
|
|
endStation: {label: "结束桩号:"},
|
|
filtersVisible: false, // 控制筛选条件是否可见
|
|
currentEvent: {},
|
|
eventSources: [
|
|
{value: 1, label: '视频AI'},
|
|
{value: 2, label: '雷达识别'},
|
|
{value: 3, label: '锥桶感应'},
|
|
{value: 4, label: '护栏碰撞'},
|
|
{value: 5, label: '扫码报警'},
|
|
{value: 6, label: '非机动车预警'},
|
|
{value: 7, label: '气象检测器'},
|
|
{value: 8, label: '边坡监测'},
|
|
{value: 9, label: '桥梁监测'}
|
|
],
|
|
directions: [
|
|
{value: 1, label: '菏泽方向'},
|
|
{value: 3, label: '济南方向'},
|
|
|
|
],
|
|
warningTypes: [
|
|
{value: 1, label: '交通拥堵'},
|
|
{value: 2, label: '行人'},
|
|
{value: 3, label: '非机动车'},
|
|
{value: 4, label: '停车'},
|
|
{value: 5, label: '倒车/逆行'},
|
|
{value: 6, label: '烟火'},
|
|
{value: 7, label: '撒落物'},
|
|
{value: 8, label: '异常天气'},
|
|
{value: 9, label: '护栏碰撞'},
|
|
{value: 10, label: '交通事故'},
|
|
{value: 11, label: '车辆故障'},
|
|
{value: 99, label: '其它'}
|
|
],
|
|
dialogVisible: false, // 不再需要
|
|
filterType: 'source', // 默认选择的筛选类型
|
|
eventData: [],
|
|
chartData: {},
|
|
type: '事件类型',
|
|
timeRange: '年',
|
|
startPole: '',
|
|
endPole: '',
|
|
// 总条数
|
|
total: 0,
|
|
// 查询参数
|
|
queryParams: {
|
|
pageNum: 1,
|
|
pageSize: 6,
|
|
direction: '',
|
|
dateTime: moment().startOf('month').format("YYYY"), // 默认选中当前月
|
|
type: '1',
|
|
warningType: '',
|
|
dateRange: [],
|
|
stakeNumber: '',
|
|
typeDateRange: [],
|
|
warningSourceArray: '',
|
|
stakeMarkArry: ['', ''], // 初始化为两个空字符串数组
|
|
endMark: ['', ''] // 初始化为两个空字符串数组
|
|
},
|
|
isDragging: false,
|
|
lastX: 0,
|
|
lastY: 0,
|
|
dialogPosition: {left: '50%', top: '10%'}, // 初始位置
|
|
rules: {
|
|
stakeMarkArry: [
|
|
{validator: this.validateStakeMarks.bind(null, 'stakeMarkArry', 'endMark'), trigger: 'blur'}
|
|
],
|
|
endMark: [
|
|
{validator: this.validateStakeMarks.bind(null, 'endMark', 'stakeMarkArry'), trigger: 'blur'}
|
|
]
|
|
}
|
|
};
|
|
},
|
|
|
|
computed: {
|
|
laneSelection: {
|
|
get() {
|
|
// 将 currentEvent.lane 的值解析为一个数组,表示哪些车道被选中
|
|
if (this.currentEvent.lane) {
|
|
return this.currentEvent.lane.split(',').map(Number);
|
|
}
|
|
return [];
|
|
},
|
|
set(newValue) {
|
|
// 更新 currentEvent.lane 的值,依据用户选择的新值
|
|
this.$set(this.currentEvent, 'lane', newValue.join(','));
|
|
}
|
|
},
|
|
datePickerType() {
|
|
switch (this.queryParams.type) {
|
|
case '1':
|
|
return 'year';
|
|
case '2':
|
|
return 'month';
|
|
default:
|
|
return '';
|
|
}
|
|
}
|
|
},
|
|
mounted() {
|
|
this.fetchData();
|
|
this.event();
|
|
document.addEventListener('click', this.handleDocumentClick);
|
|
const dialog = document.querySelector('.el-dialog');
|
|
if (dialog) {
|
|
dialog.addEventListener('mousedown', this.startDrag);
|
|
dialog.addEventListener('mousemove', this.drag);
|
|
dialog.addEventListener('mouseup', this.endDrag);
|
|
}
|
|
},
|
|
|
|
beforeDestroy() {
|
|
document.removeEventListener('click', this.handleDocumentClick);
|
|
// 清除事件监听器,防止内存泄漏
|
|
const dialogHeader = document.querySelector('.el-dialog__header');
|
|
if (dialogHeader) {
|
|
dialogHeader.removeEventListener('mousedown', this.startDrag);
|
|
window.removeEventListener('mousemove', this.drag);
|
|
window.removeEventListener('mouseup', this.endDrag);
|
|
}
|
|
},
|
|
methods: {
|
|
/** 导出按钮操作 */
|
|
handleExport() {
|
|
if (this.filterType === 'time') {
|
|
this.download('/business/warning/videoReviewListExport', {
|
|
...this.queryParams,
|
|
startDate: this.queryParams.dateTime ? this.queryParams.dateTime : moment().startOf('month').format("YYYY-MM"),
|
|
type: this.queryParams.type ? this.queryParams.type : '1',
|
|
}, `视频审核事件.xlsx`)
|
|
|
|
} else {
|
|
this.download('/business/warning/videoReviewEventSourceListExport', {
|
|
...this.queryParams,
|
|
startDate: this.queryParams.typeDateRange ? this.queryParams.typeDateRange[0] : null,
|
|
endDate: this.queryParams.typeDateRange ? this.queryParams.typeDateRange[1] : null,
|
|
startStakeMark: this.formatStakeMark(this.queryParams.stakeMarkArry),
|
|
endStakeMark: this.formatStakeMark(this.queryParams.endMark)
|
|
}, `视频审核事件.xlsx`)
|
|
}
|
|
|
|
|
|
},
|
|
handleDateChange(value) {
|
|
if (!value) return;
|
|
|
|
let formattedDate;
|
|
const momentValue = moment(value);
|
|
|
|
switch (this.queryParams.type) {
|
|
case '1': // 年
|
|
formattedDate = momentValue.format('YYYY');
|
|
break;
|
|
case '2': // 月
|
|
formattedDate = momentValue.format('YYYY-MM');
|
|
break;
|
|
default:
|
|
formattedDate = momentValue.format('YYYY-MM-DD');
|
|
}
|
|
|
|
this.$set(this.queryParams, 'dateTime', formattedDate);
|
|
},
|
|
getEventDirection(state) {
|
|
const stateMap = {
|
|
1: '菏泽方向',
|
|
3: '济南方向',
|
|
};
|
|
return stateMap[state] || '双向';
|
|
},
|
|
|
|
getWarningStateText(state) {
|
|
const stateMap = {
|
|
1: '上报',
|
|
2: '已完成',
|
|
3: '已终止',
|
|
4: '自动结束'
|
|
};
|
|
return stateMap[state] || '未知状态';
|
|
},
|
|
startDrag(e) {
|
|
if (e.target.closest('.el-dialog__header')) {
|
|
this.isDragging = true;
|
|
this.lastX = e.clientX;
|
|
this.lastY = e.clientY;
|
|
}
|
|
},
|
|
drag(e) {
|
|
if (this.isDragging) {
|
|
const dx = e.clientX - this.lastX;
|
|
const dy = e.clientY - this.lastY;
|
|
let {left, top} = this.dialogPosition;
|
|
left = `${parseInt(left) + dx}px`;
|
|
top = `${parseInt(top) + dy}px`;
|
|
|
|
this.dialogPosition = {left, top};
|
|
this.lastX = e.clientX;
|
|
this.lastY = e.clientY;
|
|
|
|
// 更新对话框的位置
|
|
document.querySelector('.el-dialog').style.transform = `translate(${left}, ${top})`;
|
|
}
|
|
},
|
|
endDrag() {
|
|
this.isDragging = false;
|
|
},
|
|
clearFilters() {
|
|
const defaultParams = {
|
|
direction: '',
|
|
warningType: '',
|
|
dateRange: [],
|
|
stakeNumber: '',
|
|
typeDateRange: [],
|
|
warningSource: '',
|
|
stakeMarkArry: ['', ''], // 初始化为两个空字符串数组
|
|
endMark: ['', ''] // 初始化为两个空字符串数组
|
|
};
|
|
// 使用 Object.assign 或扩展运算符来合并默认值和分页参数
|
|
this.queryParams = {
|
|
...defaultParams,
|
|
pageNum: this.queryParams.pageNum,
|
|
pageSize: this.queryParams.pageSize
|
|
};
|
|
// 如果表单存在,重置表单验证状态
|
|
if (this.$refs.filterForm) {
|
|
this.$refs.filterForm.resetFields();
|
|
}
|
|
},
|
|
//桩号验证
|
|
validateStakeMarks(currentKey, otherKey, rule, value, callback) {
|
|
const current = this.queryParams[currentKey];
|
|
const other = this.queryParams[otherKey];
|
|
|
|
// 检查当前桩号是否至少有一个部分被填写
|
|
const currentFilled = current.some(item => item.trim());
|
|
|
|
// 检查对应另一个桩号是否至少有一个部分被填写
|
|
const otherFilled = other.some(item => item.trim());
|
|
|
|
// 如果当前桩号有填写,则另一个桩号也必须填写,并且每个部分都不为空
|
|
if (currentFilled) {
|
|
if (!otherFilled || !other.every(item => item.trim())) {
|
|
callback(new Error(`请补全${otherKey === 'stakeMarkArry' ? '起始' : '结束'}桩号`));
|
|
} else if (!current.every(item => item.trim())) {
|
|
callback(new Error(`请补全${currentKey === 'stakeMarkArry' ? '起始' : '结束'}桩号`));
|
|
} else {
|
|
callback(); // 所有检查通过
|
|
}
|
|
} else {
|
|
// 当前桩号未填写时,允许这种情况,除非另一个桩号已经填写
|
|
if (otherFilled && !other.every(item => item.trim())) {
|
|
callback(new Error(`请补全${otherKey === 'stakeMarkArry' ? '起始' : '结束'}桩号`));
|
|
} else {
|
|
callback(); // 两者都未填写,允许这种情况
|
|
}
|
|
}
|
|
},
|
|
submitForm() {
|
|
this.$refs.filterForm.validate((valid) => {
|
|
if (valid) {
|
|
if (this.filterType === 'source') {
|
|
this.fetchData();
|
|
this.event();
|
|
} else if (this.filterType === 'section') {
|
|
this.markData();
|
|
this.event();
|
|
} else if (this.filterType === 'type') {
|
|
this.eventType();
|
|
this.event();
|
|
} else if (this.filterType === 'time') {
|
|
this.eventTime();
|
|
this.eventTimeList();
|
|
}
|
|
}
|
|
});
|
|
},
|
|
handleFilterTypeChange(value) {
|
|
// 清除不相关的筛选条件
|
|
this.clearUnrelatedFilters();
|
|
},
|
|
toggleFilters(event) {
|
|
event.stopPropagation(); // 阻止事件冒泡
|
|
this.filtersVisible = !this.filtersVisible;
|
|
},
|
|
handleDocumentClick(event) {
|
|
if (this.filtersVisible && !this.$refs.filterContainer.contains(event.target)) {
|
|
this.filtersVisible = false;
|
|
}
|
|
},
|
|
clearUnrelatedFilters() {
|
|
if (this.filterType === 'source') {
|
|
this.fetchData();
|
|
this.event();
|
|
this.clearFilters();
|
|
} else if (this.filterType === 'section') {
|
|
this.markData();
|
|
this.event();
|
|
this.clearFilters();
|
|
} else if (this.filterType === 'type') {
|
|
this.eventType();
|
|
this.event();
|
|
this.clearFilters();
|
|
} else if (this.filterType === 'time') {
|
|
this.eventTime();
|
|
this.eventTimeList();
|
|
this.clearFilters();
|
|
}
|
|
},
|
|
// 方法用于获取复核结果的文字描述
|
|
getAuditResultDescription(status) {
|
|
const auditResults = {
|
|
1: '正确',
|
|
2: '错误'
|
|
};
|
|
return auditResults[status] || '未知';
|
|
|
|
},
|
|
getEventSourceDescription(sourceCode) {
|
|
const eventSources = {
|
|
1: '视频AI',
|
|
2: '雷达识别',
|
|
3: '锥桶感应',
|
|
4: '护栏碰撞',
|
|
5: '扫码报警',
|
|
6: '非机动车预警',
|
|
7: '气象检测器',
|
|
8: '边坡监测',
|
|
9: '桥梁监测'
|
|
};
|
|
return eventSources[sourceCode] || '未知';
|
|
},
|
|
// 方法用于获取事件类型的详细描述
|
|
getEventTypeDescription(typeCode) {
|
|
const eventTypes = {
|
|
'1-1': '拥堵',
|
|
'1-2': '缓行',
|
|
'2-1': '普通行人',
|
|
'2-2': '工作人员',
|
|
'3-1': '摩托车',
|
|
'3-2': '自行车',
|
|
'3-3': '三轮车',
|
|
'4-1': '非工程车',
|
|
'4-2': '工程车',
|
|
'4-3': '主路有车',
|
|
'4-4': '匝道有车',
|
|
'4-5': '车辆故障',
|
|
'4-6': '交通事故',
|
|
'4-7': '应急车道被占用',
|
|
'4-8': '车离开应急车道',
|
|
'4-9': '其他',
|
|
'4-10': '未知',
|
|
'5-1': '倒车/逆行',
|
|
'6-1': '烟火',
|
|
'7-1': '撒落物',
|
|
'8-1': '雨',
|
|
'8-2': '冰雹',
|
|
'8-3': '风',
|
|
'8-4': '雾',
|
|
'8-5': '高温',
|
|
'8-6': '积水',
|
|
'8-7': '路面湿滑',
|
|
'8-8': '路面结冰',
|
|
'8-9': '道路能见度低',
|
|
'8-10': '道路团雾',
|
|
'9-1': '只碰撞不倾斜',
|
|
'9-2': '只倾斜无碰撞',
|
|
'9-3': '碰撞后倾斜'
|
|
};
|
|
return eventTypes[typeCode] || '未知';
|
|
},
|
|
//事件源
|
|
async fetchData() {
|
|
try {
|
|
const response = await axios.get('/business/warning/videoReviewEventSource', {
|
|
params: {
|
|
...this.queryParams, // 将所有 queryParams 属性展开到 params 对象中
|
|
startDate: this.queryParams.typeDateRange ? this.queryParams.typeDateRange[0] : null,
|
|
endDate: this.queryParams.typeDateRange ? this.queryParams.typeDateRange[1] : null
|
|
}
|
|
});
|
|
this.chartData = this.calculateChartData(response.data);
|
|
this.initCharts('事件源审核统计');
|
|
} catch (error) {
|
|
console.error('data:', error);
|
|
}
|
|
},
|
|
firstBtnClick(id) {//点击去确认/详情/处置记录等第一个按钮
|
|
axios({
|
|
url: `/perceivedEvents/warning/getWarningById`,//感知事件
|
|
method: "post",
|
|
data: {id}
|
|
}).then(async (result) => {
|
|
if (result.code != 200) return Message.error(result?.msg);
|
|
this.currentEvent = result.data;
|
|
this.dialogVisible = true;
|
|
});
|
|
},
|
|
//桩号范围
|
|
async markData() {
|
|
try {
|
|
const response = await axios.get('/business/warning/videoReviewSectionDistribution', {
|
|
params: {
|
|
...this.queryParams, // 将所有 queryParams 属性展开到 params 对象中
|
|
startDate: this.queryParams.typeDateRange ? this.queryParams.typeDateRange[0] : null,
|
|
endDate: this.queryParams.typeDateRange ? this.queryParams.typeDateRange[1] : null
|
|
}
|
|
});
|
|
this.chartData = this.calculateChartData(response.data);
|
|
this.initCharts('桩号范围');
|
|
} catch (error) {
|
|
console.error('data:', error);
|
|
}
|
|
},
|
|
//事件类型
|
|
async eventType() {
|
|
try {
|
|
const dcWarning = {
|
|
...this.queryParams,
|
|
startDate: this.queryParams.typeDateRange[0],
|
|
endDate: this.queryParams.typeDateRange[1],
|
|
startStakeMark: this.formatStakeMark(this.queryParams.stakeMarkArry),
|
|
endStakeMark: this.formatStakeMark(this.queryParams.endMark)
|
|
};
|
|
const response = await axios.post('/business/warning/videoReviewEventTypeList', dcWarning);
|
|
|
|
this.chartData = this.calculateChartData(response.data);
|
|
this.initCharts('事件类型');
|
|
} catch (error) {
|
|
console.error('data:', error);
|
|
}
|
|
},
|
|
async eventTime() {
|
|
try {
|
|
const response = await axios.get('/business/warning/videoReviewEventTime', {
|
|
params: {
|
|
...this.queryParams, // 将所有 queryParams 属性展开到 params 对象中
|
|
startDate: this.queryParams.dateTime ? this.queryParams.dateTime : moment().startOf('month').format("YYYY-MM"),
|
|
type: this.queryParams.type ? this.queryParams.type : '1',
|
|
}
|
|
});
|
|
this.chartData = this.calculateChartData(response.data);
|
|
this.initCharts('事件类型');
|
|
} catch (error) {
|
|
console.error('data:', error);
|
|
}
|
|
},
|
|
formatStakeMark(stakeMarkArry) {
|
|
if (!stakeMarkArry || !stakeMarkArry.every(item => item.trim())) {
|
|
return null;
|
|
}
|
|
// 确保每个部分都是三位数
|
|
const padNumber = (num) => String(num).padStart(3, '0');
|
|
|
|
const [km, m] = stakeMarkArry.map(padNumber);
|
|
|
|
return `K${km}+${m}`;
|
|
},
|
|
async event() {
|
|
try {
|
|
const response = await axios.get('/business/warning/videoReviewEventSourceList', {
|
|
params: {
|
|
...this.queryParams,
|
|
startDate: this.queryParams.typeDateRange ? this.queryParams.typeDateRange[0] : null,
|
|
endDate: this.queryParams.typeDateRange ? this.queryParams.typeDateRange[1] : null,
|
|
startStakeMark: this.formatStakeMark(this.queryParams.stakeMarkArry),
|
|
endStakeMark: this.formatStakeMark(this.queryParams.endMark)
|
|
// 如果有更多查询参数,可以在这里添加
|
|
}
|
|
});
|
|
|
|
this.eventData = response.rows; // 假设返回的数据结构中有 rows 和 total 字段
|
|
this.total = response.total;
|
|
} catch (error) {
|
|
console.error('event data:', error);
|
|
}
|
|
|
|
},
|
|
async eventTimeList() {
|
|
try {
|
|
const response = await axios.get('/business/warning/videoReviewEventTimeList', {
|
|
params: {
|
|
...this.queryParams, // 将所有 queryParams 属性展开到 params 对象中
|
|
startDate: this.queryParams.dateTime ? this.queryParams.dateTime : moment().startOf('month').format("YYYY-MM"),
|
|
type: this.queryParams.type ? this.queryParams.type : '1',
|
|
}
|
|
});
|
|
this.eventData = response.rows; // 假设返回的数据结构中有 rows 和 total 字段
|
|
this.total = response.total;
|
|
} catch (error) {
|
|
console.error('event data:', error);
|
|
}
|
|
|
|
},
|
|
|
|
calculateChartData(data) {
|
|
// 将数据转换为数组形式以便于操作
|
|
const formattedData = Object.keys(data).map(key => ({
|
|
name: key,
|
|
auditFlag1Count: data[key]['AuditFlag1_Count'],
|
|
auditFlag1Percentage: parseFloat(data[key]['AuditFlag1_Percentage'].replace('%', '')),
|
|
auditFlag2Count: data[key]['AuditFlag2_Count']
|
|
}));
|
|
|
|
// barChart 数据不需要排序,直接使用原始顺序
|
|
const barData = {
|
|
categories: formattedData.map(item => item.name),
|
|
auditFlag1Data: formattedData.map(item => ({name: item.name, value: item.auditFlag1Count})),
|
|
auditFlag2Data: formattedData.map(item => ({name: item.name, value: item.auditFlag2Count}))
|
|
};
|
|
|
|
// horizontalBarChart 数据按照 AuditFlag1_Percentage 排序
|
|
const sortedData = formattedData.sort((a, b) => a.auditFlag1Percentage - b.auditFlag1Percentage);
|
|
|
|
const horizontalBarData = {
|
|
data: sortedData.map(item => ({
|
|
name: item.name,
|
|
auditFlag1Value: item.auditFlag1Percentage,
|
|
}))
|
|
};
|
|
|
|
return {
|
|
bar: barData,
|
|
horizontalBar: horizontalBarData
|
|
};
|
|
},
|
|
initCharts(text) {
|
|
const barChart = echarts.init(this.$refs.barChart);
|
|
const horizontalBarChart = echarts.init(this.$refs.horizontalBarChart);
|
|
|
|
// 修改了 series 中的 stack 属性以避免堆叠
|
|
const barOption = {
|
|
title: {text: text},
|
|
tooltip: {trigger: 'axis'},
|
|
legend: {data: ['正确数', '错误数']}, // 添加图例
|
|
xAxis: {type: 'category', data: this.chartData.bar.categories},
|
|
yAxis: {type: 'value'},
|
|
series: [
|
|
{
|
|
name: '正确数', data: this.chartData.bar.auditFlag1Data.map(item => item.value), type: 'bar',
|
|
itemStyle: {
|
|
color: '#05f578' // 设置错误数的颜色为红色
|
|
},
|
|
label: {
|
|
show: true, // 显示标签
|
|
position: 'top', // 设置标签位置为顶部
|
|
formatter: '{c}', // 显示实际数值,{c}代表数值
|
|
color: '#000', // 标签文字颜色
|
|
fontSize: 12 // 文字大小
|
|
}
|
|
},
|
|
{
|
|
name: '错误数',
|
|
data: this.chartData.bar.auditFlag2Data.map(item => item.value),
|
|
type: 'bar',
|
|
itemStyle: {
|
|
color: '#f87070' // 设置错误数的颜色为红色
|
|
},
|
|
label: {
|
|
show: true,
|
|
position: 'top',
|
|
formatter: '{c}',
|
|
color: '#000',
|
|
fontSize: 12
|
|
}
|
|
}
|
|
]
|
|
};
|
|
|
|
// 修改了 tooltip formatter 函数以确保正确显示百分比
|
|
const horizontalBarOption = {
|
|
title: {text: '正确率排名'},
|
|
tooltip: {
|
|
trigger: 'item', // 改为触发于每个条目上
|
|
formatter: function (params) {
|
|
return `${params.seriesName}<br/>${params.name}: ${params.value}%`;
|
|
}
|
|
},
|
|
xAxis: {
|
|
type: 'value',
|
|
min: 0,
|
|
max: 100,
|
|
axisLabel: {
|
|
formatter: '{value}%' // 显示百分比
|
|
}
|
|
},
|
|
yAxis: {
|
|
type: 'category',
|
|
data: this.chartData.horizontalBar.data.map(item => item.name)
|
|
},
|
|
series: [
|
|
{
|
|
|
|
name: '正确率',
|
|
|
|
type: 'bar',
|
|
barWidth: '60%',
|
|
itemStyle: {
|
|
color: '#05f578'
|
|
},
|
|
data: this.chartData.horizontalBar.data.map(item => item.auditFlag1Value),
|
|
label: {
|
|
show: true,
|
|
position: 'right',
|
|
formatter: '{c}%' // 显示百分比标签
|
|
}
|
|
}
|
|
]
|
|
};
|
|
|
|
barChart.setOption(barOption);
|
|
horizontalBarChart.setOption(horizontalBarOption);
|
|
},
|
|
refreshData() {
|
|
this.fetchData();
|
|
},
|
|
exportData() {
|
|
// Implement export functionality
|
|
},
|
|
handleTypeChange(event) {
|
|
this.type = event.target.value;
|
|
},
|
|
handleTimeRangeChange(event) {
|
|
this.timeRange = event.target.value;
|
|
},
|
|
handleStartPoleChange(event) {
|
|
this.startPole = event.target.value;
|
|
},
|
|
handleEndPoleChange(event) {
|
|
this.endPole = event.target.value;
|
|
},
|
|
showDetails(item) {
|
|
// Implement details view
|
|
}
|
|
}
|
|
|
|
};
|
|
</script>
|
|
|
|
<style scoped>
|
|
.dashboard {
|
|
display: flex;
|
|
flex-direction: column;
|
|
height: 100vh;
|
|
color: #fff;
|
|
}
|
|
|
|
|
|
.charts {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
padding: 10px;
|
|
}
|
|
|
|
.bar-chart, .horizontal-bar-chart {
|
|
flex: 1;
|
|
margin: 10px;
|
|
}
|
|
|
|
|
|
/* 确保筛选条件区域不会影响到其他元素 */
|
|
.filters-container {
|
|
position: fixed;
|
|
top: 60px;
|
|
right: 10px;
|
|
width: 450px;
|
|
background-color: #e3e2e2;
|
|
padding: 10px;
|
|
border-radius: 4px;
|
|
box-shadow: 0 2px 12px 0 rgba(49, 46, 46, 0.1); /* 可选:添加阴影效果 */
|
|
z-index: 1000; /* 确保它在其他内容之上 */
|
|
}
|
|
|
|
|
|
.event-details {
|
|
display: flex;
|
|
flex-wrap: wrap; /* 允许换行 */
|
|
}
|
|
|
|
.detail-item {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
padding: 10px;
|
|
background-color: #f0f0f0;
|
|
border: 1px solid #ddd;
|
|
border-radius: 4px;
|
|
margin-bottom: 10px; /* 每个 detail-item 之间有间隔 */
|
|
flex: 1 1 30%; /* 每个项目占据一行的三分之一宽度 */
|
|
min-width: 200px; /* 设置最小宽度以防止过小 */
|
|
}
|
|
|
|
.label {
|
|
font-weight: bold;
|
|
width: 120px;
|
|
}
|
|
|
|
.value {
|
|
flex: 1;
|
|
}
|
|
|
|
.el-dialog__header {
|
|
cursor: move;
|
|
}
|
|
|
|
.selectRoad {
|
|
width: 90px;
|
|
}
|
|
|
|
::v-deep .el-input--mini .el-input__inner {
|
|
height: 35px;
|
|
line-height: 28px;
|
|
}
|
|
|
|
.table-container {
|
|
position: relative;
|
|
overflow-y: auto;
|
|
height: 480px; /* 调整高度以适应其他UI元素如按钮等 */
|
|
margin-bottom: 1em; /* 根据需要调整底部间距 */
|
|
}
|
|
</style>
|
|
|