518 lines
16 KiB
Vue
518 lines
16 KiB
Vue
<template>
|
||
<div>
|
||
<!--异常数据清洗 -->
|
||
<TableHeader
|
||
:showReset="false"
|
||
ref="TableHeaderRef"
|
||
@selectChange="selectChange"
|
||
datePicker
|
||
v-if="fullscreen"
|
||
></TableHeader>
|
||
<div
|
||
class="monitoringPoints"
|
||
:style="{
|
||
height: `calc(${prop.height} - ${headerHeight}px + ${fullscreen ? 0 : 56}px )`,
|
||
overflow: 'auto'
|
||
}"
|
||
v-loading="tableStore.table.loading"
|
||
>
|
||
<div style="flex: 1">
|
||
<div class="title">监测点统计</div>
|
||
|
||
<div>
|
||
<div class="statistics">
|
||
<div class="divBox">
|
||
<span class="iconfont icon-qiyezongshu" style="color: #57bc6e"></span>
|
||
<span class="divBox_title">监测点总数</span>
|
||
<span class="divBox_num" style="color: #57bc6e">
|
||
{{ monitoringPoints.runNum }}
|
||
</span>
|
||
</div>
|
||
<div class="divBox mt10">
|
||
<span class="iconfont icon-igw-f-warning-data" style="color: #ff6600"></span>
|
||
<span class="divBox_title">异常测点数</span>
|
||
<span class="divBox_num" style="color: #ff6600">
|
||
{{ monitoringPoints.abnormalNum }}
|
||
</span>
|
||
</div>
|
||
</div>
|
||
<div class="echartTitle">
|
||
<div class="title">异常占比</div>
|
||
<div>
|
||
{{
|
||
isNaN((monitoringPoints.abnormalNum / monitoringPoints.runNum) * 100)
|
||
? 0
|
||
: Math.floor((monitoringPoints.abnormalNum / monitoringPoints.runNum) * 10000) /
|
||
100
|
||
}}%
|
||
</div>
|
||
</div>
|
||
<div style="height: 30px">
|
||
<MyEchart :options="percentage"></MyEchart>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div style="flex: 2" class="ml15">
|
||
<div class="title">异常指标统计</div>
|
||
|
||
<div class="mb5" style="height: 40px">
|
||
<el-segmented
|
||
style="height: 100%"
|
||
v-model="segmented"
|
||
:options="segmentedList"
|
||
block
|
||
@change="change"
|
||
>
|
||
<template #default="scope">
|
||
<div>
|
||
<div
|
||
class="segmentedIcon"
|
||
:style="{
|
||
backgroundColor:
|
||
scope.item.num > 0 ? '#FF9100' : scope.item.num > 99 ? '#ff0000' : '#007D7B'
|
||
}"
|
||
>
|
||
{{ scope.item.num > 99 ? '99+' : scope.item.num }}
|
||
</div>
|
||
<div>{{ scope.item.label }}</div>
|
||
</div>
|
||
</template>
|
||
</el-segmented>
|
||
</div>
|
||
<div class="header">
|
||
<span style="width: 170px; text-align: left">指标名称</span>
|
||
<span style="flex: 1">合理范围</span>
|
||
<span style="width: 90px">异常测点数</span>
|
||
</div>
|
||
<div
|
||
:style="{
|
||
height: `calc(${prop.height} - ${headerHeight}px + ${fullscreen ? 0 : 56}px - 105px )`,
|
||
overflow: 'auto'
|
||
}"
|
||
>
|
||
<div v-for="o in abnormal.filter(item => item.remark == segmented)" class="abnormal mb10">
|
||
<span style="width: 170px; height: 24px" class="iconDiv">
|
||
<div :style="{ backgroundColor: o.ids.length > 0 ? '#FF9100' : '' }"></div>
|
||
{{ o.targetName }}
|
||
</span>
|
||
<span style="flex: 1; text-align: center">
|
||
<!-- 合理范围: -->
|
||
<span style="color: #388e3c" class="text">{{ o.rangeDesc }}</span>
|
||
</span>
|
||
<span style="width: 90px; text-align: center">
|
||
<span style="color: #388e3c" :class="` ${o.ids.length > 0 ? 'text-red' : ''}`" class="text">
|
||
{{ o.ids.length }}
|
||
</span>
|
||
</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
<script setup lang="ts">
|
||
import { ref, onMounted, provide, reactive, watch, nextTick } from 'vue'
|
||
import TableStore from '@/utils/tableStore'
|
||
import { getTimeOfTheMonth } from '@/utils/formatTime'
|
||
import MyEchart from '@/components/echarts/MyEchart.vue'
|
||
import TableHeader from '@/components/table/header/index.vue'
|
||
import { useRoute } from 'vue-router'
|
||
import { useTimeCacheStore } from '@/stores/timeCache'
|
||
import { useDictData } from '@/stores/dictData'
|
||
const dictData = useDictData()
|
||
const prop = defineProps({
|
||
w: { type: [String, Number] },
|
||
h: { type: [String, Number] },
|
||
width: { type: [String, Number] },
|
||
height: { type: [String, Number] },
|
||
timeKey: { type: [String, Number] },
|
||
timeValue: { type: Object }
|
||
})
|
||
|
||
const headerHeight = ref(57)
|
||
const TableHeaderRef = ref()
|
||
const monitoringPoints = ref({
|
||
runNum: 0,
|
||
abnormalNum: 0
|
||
})
|
||
const segmented = ref('base')
|
||
const percentage = ref({})
|
||
const segmentedList = ref([
|
||
{
|
||
label: '基础指标',
|
||
value: 'base',
|
||
num: 0
|
||
},
|
||
{
|
||
label: '稳态指标',
|
||
value: 'harmonic',
|
||
num: 0
|
||
},
|
||
{
|
||
label: '暂态指标',
|
||
value: 'event',
|
||
num: 0
|
||
}
|
||
])
|
||
const abnormal: any = ref([])
|
||
|
||
const selectChange = (showSelect: any, height: any, datePickerValue?: any) => {
|
||
headerHeight.value = height
|
||
}
|
||
|
||
// 计算是否全屏展示
|
||
const fullscreen = computed(() => {
|
||
const w = Number(prop.w)
|
||
const h = Number(prop.h)
|
||
if (!isNaN(w) && !isNaN(h) && w === 12 && h === 6) {
|
||
// 执行相应逻辑
|
||
return true
|
||
} else {
|
||
return false
|
||
}
|
||
})
|
||
|
||
const tableStore: any = new TableStore({
|
||
url: '/device-boot/dataVerify/getMonitorVerifyData',
|
||
method: 'POST',
|
||
showPage: false,
|
||
column: [],
|
||
beforeSearchFun: () => {
|
||
if (prop.timeValue && Array.isArray(prop.timeValue)) {
|
||
tableStore.table.params.searchBeginTime = prop.timeValue[0]
|
||
tableStore.table.params.searchEndTime = prop.timeValue[1]
|
||
}
|
||
},
|
||
|
||
loadCallback: () => {
|
||
segmentedList.value[0].num = 0
|
||
segmentedList.value[1].num = 0
|
||
segmentedList.value[2].num = 0
|
||
monitoringPoints.value.runNum = tableStore.table.data.runNum //总数
|
||
monitoringPoints.value.abnormalNum = tableStore.table.data.abnormalNum //异常测点数
|
||
abnormal.value = tableStore.table.data.targetList
|
||
abnormal.value.forEach(item => {
|
||
const { remark, ids } = item
|
||
if (remark === 'base') segmentedList.value[0].num += ids.length
|
||
else if (remark === 'harmonic') segmentedList.value[1].num += ids.length
|
||
else if (remark === 'event') segmentedList.value[2].num += ids.length
|
||
})
|
||
|
||
echart()
|
||
}
|
||
})
|
||
tableStore.table.params.deptId = dictData.state.area[0].id
|
||
tableStore.table.params.alarmDayLimit = 5
|
||
tableStore.table.params.warnDayLimit = 1
|
||
tableStore.table.params.lineRunFlag = 0
|
||
const echart = () => {
|
||
percentage.value = {
|
||
color: ['#FF9100'],
|
||
options: {
|
||
dataZoom: null,
|
||
toolbox: {
|
||
show: false
|
||
},
|
||
grid: {
|
||
top: '0%',
|
||
left: '0%',
|
||
right: '0%',
|
||
bottom: '0%'
|
||
},
|
||
tooltip: {
|
||
show: false
|
||
},
|
||
legend: {
|
||
show: false
|
||
},
|
||
yAxis: {
|
||
show: false,
|
||
data: ['']
|
||
},
|
||
xAxis: [
|
||
{
|
||
show: false,
|
||
type: 'value'
|
||
}
|
||
],
|
||
|
||
series: [
|
||
{
|
||
name: '异常总数',
|
||
type: 'bar',
|
||
barWidth: 12,
|
||
data: [100],
|
||
z: 0,
|
||
zlevel: 0,
|
||
itemStyle: {
|
||
normal: {
|
||
color: {
|
||
type: 'linear',
|
||
x: 0,
|
||
y: 0,
|
||
x2: 1,
|
||
y2: 0,
|
||
colorStops: [
|
||
{
|
||
offset: 1,
|
||
color: '#57bc6e' // 100% 处的颜色
|
||
}
|
||
],
|
||
global: false // 缺省为 false
|
||
}
|
||
}
|
||
}
|
||
},
|
||
{
|
||
name: '异常占比',
|
||
type: 'bar',
|
||
barWidth: 13,
|
||
data: [
|
||
(monitoringPoints.value.abnormalNum / monitoringPoints.value.runNum) * 100 == 0
|
||
? ''
|
||
: ((monitoringPoints.value.abnormalNum / monitoringPoints.value.runNum) * 100).toFixed(2)
|
||
],
|
||
z: 0,
|
||
zlevel: 0,
|
||
itemStyle: {
|
||
normal: {
|
||
color: {
|
||
type: 'linear',
|
||
x: 0,
|
||
y: 0,
|
||
x2: 1,
|
||
y2: 0,
|
||
colorStops: [
|
||
{
|
||
offset: 0,
|
||
color: '#FF9100' // 0% 处的颜色
|
||
},
|
||
{
|
||
offset: 1,
|
||
color: '#FF9100' // 100% 处的颜色
|
||
}
|
||
],
|
||
global: false // 缺省为 false
|
||
}
|
||
}
|
||
}
|
||
},
|
||
{
|
||
type: 'pictorialBar',
|
||
itemStyle: {
|
||
normal: {
|
||
color: '#fff'
|
||
}
|
||
},
|
||
symbolRepeat: 50,
|
||
// symbolMargin: 300,
|
||
symbol: 'rect',
|
||
symbolClip: true,
|
||
symbolSize: [2, 20],
|
||
symbolPosition: 'start',
|
||
symbolOffset: [0, 0],
|
||
data: [100],
|
||
z: 1,
|
||
zlevel: 0
|
||
},
|
||
{
|
||
name: '',
|
||
type: 'bar',
|
||
barGap: '-110%',
|
||
data: [100],
|
||
barWidth: 18,
|
||
|
||
itemStyle: {
|
||
normal: {
|
||
color: 'transparent',
|
||
barBorderColor: 'rgb(148,217,249)',
|
||
barBorderWidth: 1
|
||
}
|
||
},
|
||
z: 2
|
||
}
|
||
]
|
||
}
|
||
}
|
||
}
|
||
const change = () => {
|
||
tableStore.table.loading = true
|
||
setTimeout(() => {
|
||
tableStore.table.loading = false
|
||
}, 500)
|
||
}
|
||
provide('tableStore', tableStore)
|
||
|
||
onMounted(() => {
|
||
tableStore.index()
|
||
})
|
||
watch(
|
||
() => prop.timeKey,
|
||
val => {
|
||
tableStore.index()
|
||
}
|
||
)
|
||
watch(
|
||
() => prop.timeValue,
|
||
|
||
val => {
|
||
tableStore.index()
|
||
},
|
||
|
||
{
|
||
deep: true
|
||
}
|
||
)
|
||
</script>
|
||
<style lang="scss" scoped>
|
||
@import '@/assets/font/iconfont.css';
|
||
.monitoringPoints {
|
||
display: flex;
|
||
.statistics {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
justify-content: space-between;
|
||
margin-bottom: 10px;
|
||
.divBox {
|
||
width: 100%;
|
||
height: 70px;
|
||
padding: 10px;
|
||
display: flex;
|
||
|
||
.iconfont {
|
||
font-size: 40px;
|
||
margin-right: 5px;
|
||
}
|
||
.divBox_title {
|
||
font-weight: 550;
|
||
}
|
||
.divBox_num {
|
||
font-size: 20px;
|
||
font-weight: 550;
|
||
margin-left: auto;
|
||
font-family: AlimamaDongFangDaKai;
|
||
}
|
||
align-items: center;
|
||
// text-align: center;
|
||
border-radius: 5px;
|
||
&:nth-child(1) {
|
||
background-color: #eef8f0;
|
||
}
|
||
&:nth-child(2) {
|
||
background-color: #fff6ed;
|
||
}
|
||
&:nth-child(3) {
|
||
background-color: #e5f8f6;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
.detail {
|
||
flex: 1;
|
||
}
|
||
.abnormal {
|
||
width: 100%;
|
||
background-color: #f3f6f9;
|
||
border-radius: 5px;
|
||
display: flex;
|
||
// justify-content: space-between;
|
||
align-items: center;
|
||
padding: 5px 0px 5px 10px;
|
||
.iconDiv {
|
||
display: flex;
|
||
align-items: center;
|
||
div {
|
||
width: 4px;
|
||
height: 18px;
|
||
margin-right: 5px;
|
||
background-color: var(--el-color-primary);
|
||
}
|
||
}
|
||
.text {
|
||
font-weight: 700;
|
||
font-size: 16px;
|
||
font-family: 'Source Code Pro', monospace;
|
||
text-align: center;
|
||
|
||
// font-feature-settings: 'tnum';
|
||
}
|
||
}
|
||
.header {
|
||
display: flex;
|
||
text-align: center;
|
||
|
||
font-weight: 700;
|
||
padding: 5px;
|
||
}
|
||
:deep(.el-card__header) {
|
||
padding: 10px;
|
||
span {
|
||
font-weight: 600;
|
||
}
|
||
}
|
||
:deep(.el-card__body) {
|
||
padding: 10px;
|
||
}
|
||
.iconFont {
|
||
font-size: 18px;
|
||
display: inline-block;
|
||
vertical-align: middle;
|
||
}
|
||
.form {
|
||
position: relative;
|
||
.form_but {
|
||
position: absolute;
|
||
right: -22px;
|
||
}
|
||
}
|
||
.card-header {
|
||
font-size: 16px;
|
||
}
|
||
:deep(.table_name) {
|
||
color: var(--el-color-primary);
|
||
cursor: pointer;
|
||
text-underline-offset: 4px;
|
||
}
|
||
.echartTitle {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
|
||
div:nth-child(2) {
|
||
font-size: 16px;
|
||
color: #ff6600;
|
||
}
|
||
}
|
||
.title {
|
||
font-size: 14px;
|
||
font-weight: 600;
|
||
padding: 0 5px 5px;
|
||
}
|
||
|
||
:deep(.el-segmented__item-selected, ) {
|
||
clip-path: polygon(10% 0, 100% 0, 90% 100%, 0 100%);
|
||
}
|
||
:deep(.el-segmented__item, ) {
|
||
clip-path: polygon(10% 0, 100% 0, 90% 100%, 0 100%);
|
||
position: relative;
|
||
}
|
||
:deep(.el-segmented) {
|
||
clip-path: polygon(4% 0, 100% 0, 96% 100%, 0 100%);
|
||
}
|
||
.text-red {
|
||
color: #ff9100 !important;
|
||
}
|
||
|
||
.segmentedIcon {
|
||
position: absolute;
|
||
top: 1px;
|
||
right: calc(50% - 44px);
|
||
height: 18px !important;
|
||
line-height: 19px;
|
||
padding: 0 4px;
|
||
font-size: 12px;
|
||
background: #ff9100;
|
||
color: #fff;
|
||
border-radius: 8px;
|
||
}
|
||
</style>
|