修改稳态告警统计,异常数据清洗详情

This commit is contained in:
GGJ
2025-12-24 11:22:57 +08:00
parent cf7e5fa991
commit 5f4f75d9dd
6 changed files with 270 additions and 84 deletions

View File

@@ -77,7 +77,7 @@ const TableHeaderRef = ref()
const dotList: any = ref({}) const dotList: any = ref({})
const Template: any = ref({}) const Template: any = ref({})
const reportForm: any = ref('') const reportForm: any = ref('')
const name = ref('')
const templatePolicy: any = ref([]) const templatePolicy: any = ref([])
const reportFormList: any = ref([ const reportFormList: any = ref([
{ {
@@ -100,6 +100,7 @@ const tableStore = new TableStore({
beforeSearchFun: () => { beforeSearchFun: () => {
tableStore.table.params.tempId = Template.value.id tableStore.table.params.tempId = Template.value.id
tableStore.table.params.lineId = dotList.value.id tableStore.table.params.lineId = dotList.value.id
name.value = dotList.value.name
}, },
loadCallback: () => { loadCallback: () => {
tableStore.table.data.forEach((item: any) => { tableStore.table.data.forEach((item: any) => {
@@ -162,7 +163,15 @@ const handleNodeClick = (data: any, node: any) => {
} }
const exportEvent = () => { const exportEvent = () => {
exportExcel(luckysheet.getAllSheets(), '统计报表下载') // 基础获取
const now = new Date()
const year = now.getFullYear() // 4位年份
const month = now.getMonth() + 1 // 月份0-11需+1
const day = now.getDate() // 日期1-31
// 格式化YYYY - MM - DD补零
const formattedDate = `${year}${String(month).padStart(2, '0')}${String(day).padStart(2, '0')}`
exportExcel(luckysheet.getAllSheets(), name.value + formattedDate)
} }
const exportReport = () => { const exportReport = () => {
loading.value = true loading.value = true

View File

@@ -28,12 +28,7 @@
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="终端厂家:"> <el-form-item label="终端厂家:">
<el-select <el-select v-model="tableStore.table.params.manufacturer" clearable placeholder="请选择终端厂家">
v-model="tableStore.table.params.manufacturer"
clearable
placeholder="请选择终端厂家"
>
<el-option <el-option
v-for="item in terminaloption" v-for="item in terminaloption"
:key="item.id" :key="item.id"
@@ -76,7 +71,7 @@
isNaN((monitoringPoints.abnormalNum / monitoringPoints.runNum) * 100) isNaN((monitoringPoints.abnormalNum / monitoringPoints.runNum) * 100)
? 0 ? 0
: Math.floor((monitoringPoints.abnormalNum / monitoringPoints.runNum) * 10000) / : Math.floor((monitoringPoints.abnormalNum / monitoringPoints.runNum) * 10000) /
100 100
}}% }}%
</div> </div>
</div> </div>
@@ -128,7 +123,7 @@
<MyEchart :options="options"></MyEchart> <MyEchart :options="options"></MyEchart>
</div> </div>
<el-form :inline="true" class="form"> <el-form :inline="true" class="form">
<el-form-item label="告警持续天数"></el-form-item> <!-- <el-form-item label="告警持续天数"></el-form-item>
<el-form-item label="告警阀值(天)"> <el-form-item label="告警阀值(天)">
<el-input-number <el-input-number
v-model="tableStore.table.params.alarmDayLimit" v-model="tableStore.table.params.alarmDayLimit"
@@ -144,6 +139,13 @@
:step="1" :step="1"
step-strictly step-strictly
/> />
</el-form-item> -->
<el-form-item label="数据筛选">
<el-input
placeholder="请输入监测点名称/终端名称"
v-model="tableStore.table.params.searchValue"
clearable
></el-input>
</el-form-item> </el-form-item>
<el-form-item class="form_but"> <el-form-item class="form_but">
<el-button type="primary" @click="MonitorVerify" icon="el-icon-Search">查询</el-button> <el-button type="primary" @click="MonitorVerify" icon="el-icon-Search">查询</el-button>
@@ -493,7 +495,7 @@ tableStore.table.params.name = ''
provide('tableStore', tableStore) provide('tableStore', tableStore)
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@import '@/assets/font/iconfont.css'; @import '@/assets/font/iconfont.css';
.card-list { .card-list {
display: flex; display: flex;
.monitoringPoints { .monitoringPoints {

View File

@@ -1,7 +1,7 @@
<template> <template>
<el-dialog draggable width="1350px" class="cn-operate-dialog" v-model="dialogVisible" :title="title"> <el-dialog draggable width="1400px" class="cn-operate-dialog" v-model="dialogVisible" :title="title">
<div style="display: flex" v-loading="loading"> <div style="display: flex" v-loading="loading">
<div :style="height1" class="mr10 box" style="width: 500px"> <div :style="height1" class="mr10 box" style="width: 550px">
<vxe-table <vxe-table
height="auto" height="auto"
:data="TableData" :data="TableData"
@@ -11,45 +11,100 @@
@current-change="currentChangeEvent" @current-change="currentChangeEvent"
> >
<vxe-column type="seq" title="序号" width="60px"></vxe-column> <vxe-column type="seq" title="序号" width="60px"></vxe-column>
<vxe-column field="date" title="日期"></vxe-column> <!-- <vxe-column field="date" title="日期"></vxe-column> -->
<vxe-column field="bdName" title="所属电站(场站)" width="120px"></vxe-column> <vxe-column field="bdName" title="所属电站(场站)"></vxe-column>
<vxe-column field="monitorName" title="监测点名称" width="120px"></vxe-column> <vxe-column field="monitorName" title="监测点名称"></vxe-column>
<vxe-column field="timeSum" title="告警时间(分钟)" width="80px"></vxe-column> <vxe-column field="timeSum" title="告警天数" width="80px">
<template v-slot="{ row }">
{{ row.dateTargetList?.length }}
</template>
</vxe-column>
</vxe-table> </vxe-table>
</div> </div>
<div :style="height" style="width: 820px" v-loading="loading1"> <div style="width: 820px" v-loading="loading1">
<vxe-table <el-form :inline="true" class="form">
height="auto" <el-form-item label="统计日期">
:data="TableData1.slice((pageNum - 1) * pageSize, pageNum * pageSize)" <el-select
v-bind="defaultAttribute" v-model="timeList"
> clearable
<vxe-column type="seq" title="序号" width="80px"> placeholder="请选择告警天数"
<template #default="{ rowIndex }"> style="width: 180px"
<span>{{ (pageNum - 1) * pageSize + rowIndex + 1 }}</span> @change="change"
</template> >
</vxe-column> <el-option
<vxe-column field="time" title="时间" :formatter="formatter"></vxe-column> v-for="item in dateList"
<vxe-column field="targetName" title="指标类型" min-width="100px"></vxe-column> :key="item.date"
<vxe-column field="phaseType" title="相别" width="80px"> :label="item.date"
<template v-slot="{ row }"> :value="item.date"
{{ row.phaseType == 'T' ? '/' : row.phaseType }} />
</template> </el-select>
</vxe-column> </el-form-item>
<el-form-item label="统计指标" v-if="numKey != 0">
<el-select
v-model="targetKey"
filterable
clearable
placeholder="请选择指标"
style="width: 150px"
>
<el-option
v-for="item in targetList"
:key="item.key"
:label="item.targetName"
:value="item.key"
/>
</el-select>
</el-form-item>
<vxe-column field="type" title="数据类型" width="105px" :formatter="formatter"></vxe-column> <el-form-item class="form_but">
<vxe-column field="val" title="值" width="85px" :formatter="formatter"></vxe-column> <span class="mr20">
<vxe-column field="overLimitValue" title="限值" width="85px" :formatter="formatter"></vxe-column> 告警时间
</vxe-table> <span class="title">{{ timeSum }}</span>
<div class="table-pagination"> 分钟
<el-pagination </span>
v-model:currentPage="pageNum"
v-model:page-size="pageSize" <el-button type="primary" @click="init" icon="el-icon-Search">查询</el-button>
:page-sizes="[10, 20, 50, 100, 200]" </el-form-item>
background </el-form>
layout="sizes,total, ->, prev, pager, next, jumper" <div :style="height">
:total="TableData1.length" <vxe-table
></el-pagination> height="auto"
:data="TableData1?.slice((pageNum - 1) * pageSize, pageNum * pageSize)"
v-bind="defaultAttribute"
>
<vxe-column type="seq" title="序号" width="80px">
<template #default="{ rowIndex }">
<span>{{ (pageNum - 1) * pageSize + rowIndex + 1 }}</span>
</template>
</vxe-column>
<vxe-column field="time" width="150px" title="时间" :formatter="formatter"></vxe-column>
<vxe-column field="targetName" title="指标类型" min-width="100px"></vxe-column>
<vxe-column field="phaseType" title="相别" width="80px">
<template v-slot="{ row }">
{{ row.phaseType == 'T' ? '/' : row.phaseType }}
</template>
</vxe-column>
<vxe-column field="type" title="数据类型" width="105px" :formatter="formatter"></vxe-column>
<vxe-column field="val" title="值" width="85px" :formatter="formatter"></vxe-column>
<vxe-column
field="overLimitValue"
title="限值"
width="85px"
:formatter="formatter"
></vxe-column>
</vxe-table>
<div class="table-pagination">
<el-pagination
v-model:currentPage="pageNum"
v-model:page-size="pageSize"
:page-sizes="[10, 20, 50, 100, 200]"
background
layout="sizes,total, ->, prev, pager, next, jumper"
:total="TableData1?.length || 0"
></el-pagination>
</div>
</div> </div>
</div> </div>
</div> </div>
@@ -57,44 +112,47 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, inject } from 'vue' import { ref, inject } from 'vue'
import { useDictData } from '@/stores/dictData' import { reactive } from 'vue'
import { defaultAttribute } from '@/components/table/defaultAttribute' import { defaultAttribute } from '@/components/table/defaultAttribute'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import { monitorLimitTable, monitorLimitTableDetail } from '@/api/device-boot/dataVerify' import { monitorLimitTable, monitorLimitTableDetail } from '@/api/device-boot/dataVerify'
import { mainHeight } from '@/utils/layout' import { mainHeight } from '@/utils/layout'
const dictData = useDictData()
const StatisList = dictData.getBasicData('Steady_Statis')
const dialogVisible = ref(false) const dialogVisible = ref(false)
const height1 = mainHeight(-110, 2) const height1 = mainHeight(-110, 2)
const height = mainHeight(10, 2) const height = mainHeight(90, 2)
const tableRef = ref() const tableRef = ref()
const title = ref('') const title = ref('')
const loading = ref(false) const loading = ref(false)
const loading1 = ref(false) const loading1 = ref(false)
const TableData = ref([]) const TableData = ref([])
const TableData1 = ref([]) const TableData1 = ref([])
const dateList: any = ref([])
const pageNum = ref(1) const pageNum = ref(1)
const pageSize = ref(20) const pageSize = ref(20)
const numKey = ref(0) const numKey = ref(0)
const targetKey = ref('') const targetKey = ref('')
const clickRow = ref({}) const targetList: any = ref([])
const timeSum = ref('')
const timeList = ref('')
const showColumn = ref(true)
const open = (data: anyObj, time: string[], num: number) => { const open = (data: anyObj, time: string[], num: number) => {
// title.value = (num == 0 ? data.targetName : data.monitorName) + '_告警详情展示' // title.value = (num == 0 ? data.targetName : data.monitorName) + '_告警监测点详情'
loading.value = true loading.value = true
title.value = '告警监测点详情' title.value = '告警监测点详情'
numKey.value = num
targetKey.value = data.key
TableData.value = [] TableData.value = []
numKey.value = num
targetKey.value = data.key || ''
monitorLimitTable({ monitorLimitTable({
monitorIds: num == 0 ? data.ids : [data.monitorId], monitorIds: num == 0 ? data.ids : [data.monitorId],
targetKey: num == 0 ? data.key : '', targetKey: num == 0 ? data.key : '',
searchBeginTime: time[0], searchBeginTime: time[0],
searchEndTime: time[1] searchEndTime: time[1]
}) })
.then(async(res) => { .then(async res => {
TableData.value = res.data TableData.value = res.data
await tableRef.value.setCurrentRow(TableData.value[0]) await tableRef.value.setCurrentRow(TableData.value[0])
await currentChangeEvent() await currentChangeEvent()
loading.value = false loading.value = false
}) })
.catch(() => { .catch(() => {
@@ -103,16 +161,28 @@ const open = (data: anyObj, time: string[], num: number) => {
dialogVisible.value = true dialogVisible.value = true
} }
const currentChangeEvent = () => { const currentChangeEvent = () => {
let data = tableRef.value.getCurrentRecord()
dateList.value = data.dateTargetList
targetList.value = dateList.value[0].targetKeys
timeList.value = data.dateTargetList[0].date
init()
}
const init = () => {
if (timeList.value == '') return ElMessage.warning('请选择告警天数!')
loading1.value = true loading1.value = true
clickRow.value = tableRef.value.getCurrentRecord()
TableData1.value = [] TableData1.value = []
let data = tableRef.value.getCurrentRecord()
monitorLimitTableDetail({ monitorLimitTableDetail({
monitorIds: [clickRow.value.monitorId], monitorIds: [data.monitorId],
searchBeginTime: clickRow.value.date, searchBeginTime: timeList.value,
targetKey: numKey.value == 0 ? targetKey.value : '' targetKey: targetKey.value || '' //numKey.value == 0 ? targetKey.value : ''
}) })
.then(res => { .then(res => {
TableData1.value = res.data timeSum.value = res.data.timeSum
TableData1.value = res.data.time
showColumn.value = res.data[0]?.featureAmplitude == null ? true : false
loading1.value = false loading1.value = false
pageNum.value = 1 pageNum.value = 1
}) })
@@ -120,9 +190,37 @@ const currentChangeEvent = () => {
loading1.value = false loading1.value = false
}) })
} }
const change = () => {
if (timeList.value == '') targetKey.value = ''
let list = dateList.value
.filter(item => timeList.value == item.date)
?.map(item => {
return item.targetKeys
})
.flat()
targetList.value = deduplicateByKey(list)
if (!targetList.value.some(item => item.key == targetKey.value) ) {
targetKey.value = ''
}
console.log('🚀 ~ change ~ targetList.value:', targetList.value)
}
// 核心去重逻辑
function deduplicateByKey(data) {
// 使用Map存储唯一key保证顺序且去重
const uniqueMap = new Map()
data.forEach(item => {
if (!uniqueMap.has(item.key)) {
uniqueMap.set(item.key, item)
}
})
// 转换为数组并按sort排序
const deduplicatedData = Array.from(uniqueMap.values())
return deduplicatedData
}
const formatter = (row: any) => { const formatter = (row: any) => {
if (row.column.field == 'time') { if (row.column.field == 'time') {
return clickRow.value.date + ' ' + row.cellValue return timeList.value + ' ' + row.cellValue
} else if (row.column.field == 'type') { } else if (row.column.field == 'type') {
return row.cellValue === 'null' ? '/' : row.cellValue return row.cellValue === 'null' ? '/' : row.cellValue
} else { } else {
@@ -145,7 +243,21 @@ defineExpose({ open })
} }
:deep(.box) { :deep(.box) {
.row--current { .row--current {
//background-color: var(--el-color-primary-light-8) !important; // background-color: var(--el-color-primary-light-8) !important;
} }
} }
.form {
position: relative;
.form_but {
position: absolute;
right: -22px;
}
}
.title {
font-weight: 600;
font-size: 16px;
}
:deep(.el-select) {
min-width: 100px !important;
}
</style> </style>

View File

@@ -1,7 +1,7 @@
<template> <template>
<el-dialog draggable width="1550px" class="cn-operate-dialog" v-model="dialogVisible" :title="title"> <el-dialog draggable width="1550px" class="cn-operate-dialog" v-model="dialogVisible" :title="title">
<div style="display: flex" v-loading="loading"> <div style="display: flex" v-loading="loading">
<div :style="height1" class="mr10 box" style="width: 600px"> <div :style="height1" class="mr10 box" style="width: 550px">
<vxe-table <vxe-table
height="auto" height="auto"
:data="TableData" :data="TableData"
@@ -16,14 +16,14 @@
<vxe-column field="monitorName" title="监测点名称"></vxe-column> <vxe-column field="monitorName" title="监测点名称"></vxe-column>
<vxe-column field="timeSum" title="异常天数" width="80px"> <vxe-column field="timeSum" title="异常天数" width="80px">
<template v-slot="{ row }"> <template v-slot="{ row }">
{{ row.dateList?.length }} {{ row.dateTargetList?.length }}
</template> </template>
</vxe-column> </vxe-column>
<!-- <vxe-column field="errCount" title="异常次数" width="80px"></vxe-column> --> <!-- <vxe-column field="errCount" title="异常次数" width="80px"></vxe-column> -->
</vxe-table> </vxe-table>
</div> </div>
<div style="width: 920px" v-loading="loading1"> <div style="width: 970px" v-loading="loading1">
<el-form :inline="true" class="form"> <el-form :inline="true" class="form">
<el-form-item label="统计日期"> <el-form-item label="统计日期">
<el-select <el-select
@@ -32,15 +32,45 @@
collapse-tags collapse-tags
clearable clearable
placeholder="请选择异常天数" placeholder="请选择异常天数"
style="width: 200px" style="width: 180px"
@change="change"
> >
<el-option v-for="item in dateList" :key="item" :label="item" :value="item" /> <el-option
v-for="item in dateList"
:key="item.date"
:label="item.date"
:value="item.date"
/>
</el-select>
</el-form-item>
<el-form-item label="统计指标" v-if="numKey != 0">
<el-select
v-model="targetKey"
filterable
clearable
placeholder="请选择指标"
style="width: 150px"
>
<el-option
v-for="item in targetList"
:key="item.key"
:label="item.targetName"
:value="item.key"
/>
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item class="form_but"> <el-form-item class="form_but">
<span class="mr20">异常时间<span class="title">{{ timeSum }}</span>分钟</span> <span class="mr20">
<span class="mr10">异常次数<span class="title">{{ errCount }}</span></span> 异常时间
<span class="title">{{ timeSum }}</span>
分钟
</span>
<span class="mr10">
异常次数
<span class="title">{{ errCount }}</span>
</span>
<el-button type="primary" @click="init" icon="el-icon-Search">查询</el-button> <el-button type="primary" @click="init" icon="el-icon-Search">查询</el-button>
</el-form-item> </el-form-item>
@@ -136,17 +166,19 @@ const pageNum = ref(1)
const pageSize = ref(20) const pageSize = ref(20)
const numKey = ref(0) const numKey = ref(0)
const targetKey = ref('') const targetKey = ref('')
const targetList: any = ref([])
const errCount = ref('') const errCount = ref('')
const timeSum = ref('') const timeSum = ref('')
const timeList = ref([]) const timeList = ref([])
const showColumn = ref(true) const showColumn = ref(true)
const open = (data: anyObj, time: string[], num: number) => { const open = (data: anyObj, time: string[], num: number) => {
// title.value = (num == 0 ? data.targetName : data.monitorName) + '_异常监测点详情' // title.value = (num == 0 ? data.targetName : data.monitorName) + '_异常监测点详情'
loading.value = true loading.value = true
title.value = '异常监测点详情' title.value = '异常监测点详情'
TableData.value = [] TableData.value = []
numKey.value = num numKey.value = num
targetKey.value = data.key targetKey.value = data.key || ''
monitorAbnormalTable({ monitorAbnormalTable({
monitorIds: num == 0 ? data.ids : [data.monitorId], monitorIds: num == 0 ? data.ids : [data.monitorId],
targetKey: num == 0 ? data.key : '', targetKey: num == 0 ? data.key : '',
@@ -167,13 +199,14 @@ const open = (data: anyObj, time: string[], num: number) => {
const currentChangeEvent = () => { const currentChangeEvent = () => {
let data = tableRef.value.getCurrentRecord() let data = tableRef.value.getCurrentRecord()
dateList.value = data.dateList dateList.value = data.dateTargetList
console.log('🚀 ~ currentChangeEvent ~ dateList.value:', dateList.value) targetList.value = dateList.value[0].targetKeys
timeList.value = [data.dateList[0]] timeList.value = [data.dateTargetList[0].date]
init() init()
} }
const init = () => { const init = () => {
if(timeList.value.length == 0) return ElMessage.warning('请选择异常天数!') if (timeList.value.length == 0) return ElMessage.warning('请选择异常天数!')
loading1.value = true loading1.value = true
TableData1.value = [] TableData1.value = []
let data = tableRef.value.getCurrentRecord() let data = tableRef.value.getCurrentRecord()
@@ -181,7 +214,7 @@ const init = () => {
monitorIds: [data.monitorId], monitorIds: [data.monitorId],
time: timeList.value, time: timeList.value,
searchBeginTime: data.date, searchBeginTime: data.date,
targetKey: numKey.value == 0 ? targetKey.value : '' targetKey: targetKey.value || '' //numKey.value == 0 ? targetKey.value : ''
}) })
.then(res => { .then(res => {
errCount.value = res.data.errCount errCount.value = res.data.errCount
@@ -195,6 +228,33 @@ const init = () => {
loading1.value = false loading1.value = false
}) })
} }
const change = () => {
if (timeList.value.length == 0) targetKey.value = ''
let list = dateList.value
.filter(item => timeList.value.includes(item.date))
?.map(item => {
return item.targetKeys
})
.flat()
targetList.value = deduplicateByKey(list)
if (!targetList.value.some(item => item.key == targetKey.value)) {
targetKey.value = ''
}
}
// 核心去重逻辑
function deduplicateByKey(data) {
// 使用Map存储唯一key保证顺序且去重
const uniqueMap = new Map()
data.forEach(item => {
if (!uniqueMap.has(item.key)) {
uniqueMap.set(item.key, item)
}
})
// 转换为数组并按sort排序
const deduplicatedData = Array.from(uniqueMap.values())
return deduplicatedData
}
const formatter = (row: any) => { const formatter = (row: any) => {
return row.cellValue == null ? '/' : (row.cellValue - 0).toFixed(2) return row.cellValue == null ? '/' : (row.cellValue - 0).toFixed(2)
} }
@@ -224,8 +284,11 @@ defineExpose({ open })
right: -22px; right: -22px;
} }
} }
.title{ .title {
font-weight: 600; font-weight: 600;
font-size: 16px; font-size: 16px;
} }
:deep(.el-select) {
min-width: 100px !important;
}
</style> </style>

View File

@@ -351,17 +351,17 @@ const summonFlagList = [
] ]
//定义终端模型下拉框数据 //定义终端模型下拉框数据
const terminalModelList = [ const terminalModelList = [
{ {
id: '0', id: '0',
name: '虚拟终端' name: '虚拟终端'
}, },
{ {
id: '1', id: '1',
name: '虚拟终端' name: '实际终端'
}, },
{ {
id: '2', id: '2',
name: '虚拟终端' name: '离线'
} }
] ]
//定义通讯状态下拉框数据 //定义通讯状态下拉框数据

View File

@@ -4,7 +4,7 @@
<el-upload <el-upload
ref="upload" ref="upload"
action="" action=""
:auto-upload="false" :auto-upload="false"
:show-file-list="false" :show-file-list="false"
:limit="1" :limit="1"
:on-change="beforeUpload" :on-change="beforeUpload"