联调市详情页面

This commit is contained in:
GGJ
2024-04-29 16:37:07 +08:00
parent 0c7b5ff4d6
commit 830d2017a5
20 changed files with 1295 additions and 141 deletions

View File

@@ -26,7 +26,7 @@
"echarts": "^5.4.3",
"echarts-gl": "^2.0.9",
"echarts4": "npm:echarts@^4.9.0",
"element-plus": "2.5.3",
"element-plus": "^2.7.2",
"exceljs": "^4.4.0",
"file-saver": "^2.0.5",
"html2canvas": "^1.4.1",

View File

@@ -85,3 +85,24 @@ export function getEventStatisticsList(data: any) {
export function getEventReason(data: any) {
return request({ url: '/event-boot/areaAnalysis/getEventReason', method: 'post', data })
}
// 暂态事件统计
export function getGeneralSituation(data: any) {
return request({ url: '/event-boot/report/getGeneralSituation', method: 'post', data })
}
// 获取实际运行终端综合信息
export function getPracticalRunDeviceInfo(data: any) {
return request({ url: '/device-boot/deviceInfo/getPracticalRunDeviceInfo', method: 'post', data })
}
// 监测终端状态
export function getGridDiagramCityDev(data: any) {
return request({ url: '/device-boot/gridDiagram/getGridDiagramCityDev', method: 'post', data })
}
// 稳态指标合格率详情
export function qualifiedDetail(data: any) {
return request({ url: '/harmonic-boot/grid/qualifiedDetail', method: 'post', data })
}
// 综合评估趋势对比
export function getAssessTrend(data: any) {
return request({ url: '/harmonic-boot/grid/getAssessTrend', method: 'post', data })
}

View File

@@ -34,6 +34,7 @@ span:focus {
.el-dialog {
margin-top: 50vh !important;
transform: translateY(-50%);
padding: 0px !important;
overflow: hidden;
}
.el-dialog__wrapper {

View File

@@ -113,7 +113,7 @@ const handlerBar = (options: any) => {
options.series.forEach((item: any) => {
if (item.type === 'bar') {
item.barMinHeight = 10
item.barMaxWidth = 30
item.barMaxWidth = 20
item.itemStyle = Object.assign(
{
color: (params: any) => {
@@ -163,8 +163,8 @@ const handlerYAxis = () => {
if (Array.isArray(props.options?.yAxis)) {
return props.options?.yAxis.map((item: any) => {
return {
...item,
...temp
...temp,
...item
}
})
} else {

View File

@@ -9,7 +9,7 @@ defineOptions({
name: 'Area'
})
import { useDictData } from '@/stores/dictData'
const emit = defineEmits(['changeName'])
const emit = defineEmits(["changeValue"])
const cascaderProps = {
label: 'name',
value: 'id',
@@ -18,7 +18,7 @@ const cascaderProps = {
}
const cascader = ref()
const dictData = useDictData()
const options = dictData.state.area
const options:any = dictData.state.area
const areaName = ref(dictData.state.area[0].name)
const jbName = ref(dictData.state.area[0].name)
const change = (e: any) => {
@@ -27,7 +27,8 @@ const change = (e: any) => {
} else if (cascader.value.getCheckedNodes()[0]?.pathLabels.length >= 2) {
areaName.value = cascader.value.getCheckedNodes()[0].pathLabels[1]
}
emit('changeName', cascader.value.getCheckedNodes()[0].label)
emit('changeValue', cascader.value.getCheckedNodes()[0])
}
const changeName = () => {
return jbName.value

View File

@@ -196,7 +196,7 @@ body,
}
.cn-operate-dialog .el-dialog__footer {
padding: 10px var(--el-dialog-padding-primary);
padding: 15px;
box-shadow: var(--el-box-shadow);
position: absolute;
width: 100%;

View File

@@ -29,20 +29,26 @@
}
}
}
.el-dialog {
padding: 0px !important;
}
.el-dialog__header {
background: var(--el-color-primary);
margin-right: 0;
padding: 15px;
margin-right: 0px;
.el-dialog__headerbtn {
top: 5px;
.el-icon {
color: var(--el-color-white);
}
}
.el-dialog__title {
color: var(--el-color-white);
}
}
.el-table {

View File

@@ -0,0 +1,44 @@
<template>
<!-- 综合评估详情 -->
<el-dialog draggable title="指标合格率统计详情" v-model="dialogVisible" width="1400px">
<div>
<vxe-table v-bind="defaultAttribute" ref="vxeRef" height="600px" :data="tableData">
<vxe-column field="substationName" title="变电站名称" />
<vxe-column field="lineName" title="监测点名称" />
<vxe-colgroup title="各指标合格率">
<vxe-column field="freqDev" title=" 频率偏差" :formatter="formatter" />
<vxe-column field="plt" title="闪变" :formatter="formatter" />
<vxe-column field="ubalance" title="三相电压不平衡度" :formatter="formatter" />
<vxe-column field="vdev" title="电压偏差" :formatter="formatter" />
<vxe-column field="vthd" title="电压总谐波畸变率" :formatter="formatter" />
</vxe-colgroup>
</vxe-table>
</div>
</el-dialog>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import MyEChart from '@/components/echarts/MyEchart.vue'
import { defaultAttribute } from '@/components/table/defaultAttribute'
import { qualifiedDetail } from '@/api/device-boot/panorama'
const dialogVisible: any = ref(false)
const tableData: any = ref([])
const open = async (row: any) => {
qualifiedDetail(row).then(res => {
tableData.value = res.data
})
dialogVisible.value = true
}
const formatter = (row: any) => {
if (row.cellValue == 3.14159) {
return '/'
} else {
return row.cellValue
}
}
defineExpose({ open })
</script>
<style lang="scss" scoped></style>

View File

@@ -0,0 +1,201 @@
<template>
<div class="boxLeft">
<!-- 监测点 -->
<el-card>
<template #header>
<div class="card-header">
<span>暂态事件统计</span>
</div>
</template>
<div :style="boxHeight">
<el-segmented v-model="segmentedValue" :options="segmentedList" block />
<el-row style="height: calc(100% - 62px); margin-top: 20px">
<el-col :span="12" class="col" style="border-right: 1px solid #ccc">
<div>
<span>异常问题总数</span>
<span style="color: #2dcd28">60</span>
</div>
<div>
<span style="width: 120px">已关联工单数</span>
<span style="color: #81b337">60</span>
</div>
<div>
<span style="width: 120px"> 工单转换率</span>
<span style="color: #338dff">60%</span>
</div>
</el-col>
<el-col :span="12" class="col">
<div>
<span>异常问题总数</span>
<span style="color: #2dcd28">60</span>
</div>
<div>
<span style="width: 120px">已关联工单数</span>
<span style="color: #81b337">60</span>
</div>
<div>
<span style="width: 120px"> 工单转换率</span>
<span style="color: #338dff">60%</span>
</div>
</el-col>
</el-row>
</div>
</el-card>
</div>
</template>
<script setup lang="ts">
import { onMounted, reactive, ref, provide } from 'vue'
import MyEChart from '@/components/echarts/MyEchart.vue'
import { useDictData } from '@/stores/dictData'
import { mainHeight } from '@/utils/layout'
import {} from '@/api/device-boot/panorama'
const dictData = useDictData()
const boxHeight: any = mainHeight(220, 3)
const formRow: any = ref({})
const segmentedValue = ref('0')
const segmentedList = [
{
label: '监督计划',
value: '0'
},
{
label: '系统监测',
value: '1'
},
{
label: '现场测试',
value: '2'
},
{
label: '用户投诉',
value: '3'
}
]
const info = (row: any) => {
let form = {
...row,
id: row.orgNo,
deptIndex: row.orgNo,
orgId: row.orgNo,
ids: [],
statisticalType: dictData.getBasicData('Statistical_Type', ['Report_Type'])[0],
isUpToGrid: row.isUpToGrid,
monitorFlag: row.isUpToGrid
}
let loadType = dictData.getBasicData('Statistical_Type').find(item => item.code == 'Load_Type')
formRow.value = form
}
defineExpose({ info })
</script>
<style lang="scss" scoped>
.boxLeft {
// width: calc(50% - 10px);
width: 50%;
padding: 10px 10px 10px 10px;
font-size: 13px;
overflow: hidden;
}
.title {
// height: ;
display: flex;
justify-content: space-between;
font-size: 16px;
height: 22px;
line-height: 23px;
padding-left: 5px;
width: 100%;
background-image: linear-gradient(to right, #a4e5da, #fff);
.info {
font-weight: normal;
display: flex;
font-size: 12px;
cursor: pointer;
}
}
.evaluate {
height: 60px;
border: 1px solid #ccc;
margin: 10px 0;
padding: 10px 0;
display: flex;
justify-content: space-around;
text-align: center;
overflow-x: auto;
overflow-y: hidden;
}
.boxR {
border: 1px solid #ccc;
margin: 10px 0;
padding: 5px;
.num {
color: #2478f2;
}
.top {
display: flex;
justify-content: space-between;
}
.bottom {
margin: 0;
border: 0px;
}
}
:deep(.el-select) {
min-width: 120px;
}
.col {
display: flex;
flex-wrap: wrap;
justify-content: space-around;
align-items: center;
}
.imgR {
position: absolute;
padding: 10px;
top: calc(50% - 80px);
left: -23px;
z-index: 1;
height: 200px;
cursor: pointer;
}
:deep(.el-card) {
.el-card__header {
padding: 5px 20px;
font-size: 1.2rem;
&::before {
width: 0.3rem;
height: 1.2rem;
margin-top: 0.3rem;
position: absolute;
content: '';
background: var(--el-color-primary);
border-radius: 0.02rem;
left: 1.2rem;
}
}
.el-card__body {
padding: 10px;
}
}
.col {
display: grid;
grid-template-columns: 1fr;
grid-template-rows:repeat(3,auto);
text-align: center;
// flex-wrap: wrap;
// justify-content: space-around;
// align-items: center;
}
:deep(.el-segmented) {
--el-border-radius-base: 16px;
}
</style>

View File

@@ -0,0 +1,324 @@
<template>
<div class="boxLeft" :style="height">
<!-- 在线监测规模 -->
<el-card>
<template #header>
<div class="card-header">
<span>在线监测规模</span>
</div>
</template>
<MyEChart :style="boxHeight" :options="onlineCharts" />
</el-card>
<!-- 监测终端状态 -->
<el-card>
<template #header>
<div class="card-header">
<span>监测终端状态</span>
</div>
</template>
<div>
<MyEChart :style="boxHeight" :options="terminalCharts" />
</div>
</el-card>
<!-- 监测点 -->
<el-card>
<template #header>
<div class="card-header">
<span>监测点</span>
</div>
</template>
<MyEChart :style="boxHeight" :options="dotCharts" />
</el-card>
</div>
</template>
<script setup lang="ts">
import { onMounted, reactive, ref, provide } from 'vue'
import MyEChart from '@/components/echarts/MyEchart.vue'
import { useDictData } from '@/stores/dictData'
import { mainHeight } from '@/utils/layout'
import { getPracticalRunDeviceInfo, getGridDiagramCityDev, getGridDiagramLineData } from '@/api/device-boot/panorama'
const dictData = useDictData()
const height = mainHeight(20)
const boxHeight: any = mainHeight(220, 3)
const formRow: any = ref({})
const terminalCharts: any = ref({})
const dotCharts = ref()
const onlineCharts = ref()
const info = (row: any) => {
let form = {
...row,
id: row.orgNo,
deptIndex: row.orgNo,
orgId: row.orgNo,
ids: [],
statisticalType: dictData.getBasicData('Statistical_Type', ['Report_Type'])[0],
isUpToGrid: row.isUpToGrid,
monitorFlag: row.isUpToGrid
}
let loadType = dictData.getBasicData('Statistical_Type').find(item => item.code == 'Load_Type')
formRow.value = form
// 变电站
getPracticalRunDeviceInfo(form).then(res => {
onlineCharts.value = {
tooltip: {},
yAxis: {
type: 'value'
},
xAxis: {
type: 'category',
data: ['变电站', '监测装置', '监测点'],
axisLabel: {
color: '#000',
fontSize: 12
}
},
grid: {
top: '30px',
left: '10px',
right: '20px'
},
options: {
dataZoom: null,
series: [
{
name: '',
type: 'bar',
data: [
res.data[0].subIndexes.length,
res.data[0].deviceIndexes.length,
res.data[0].lineIndexes.length
],
label: {
show: true,
position: 'top',
fontSize: 12
}
}
]
}
}
})
// 终端
getGridDiagramCityDev({ ...form, deviceInfoParam: form, pageNum: 1, pageSize: 1000 }).then(res => {
terminalCharts.value = {
title: {
text: ''
},
legend: {
show: true,
right: 90
},
xAxis: {
data: ['运行', '检修', '退运']
},
yAxis: [
{
name: '个数',
type: 'value'
},
{
type: 'value',
name: '在线率',
splitLine: {
show: false
},
max: 100
}
],
grid: {
top: '35px',
left: '20px',
right: '20px'
},
options: {
dataZoom: null,
series: [
{
name: '终端个数',
type: 'bar',
data: [res.data[0], res.data[2], res.data[4]],
label: {
show: true,
position: 'top',
fontSize: 12
}
},
{
name: '在线率',
type: 'bar',
yAxisIndex: 1,
data: [res.data[1]],
label: {
show: true,
position: 'top',
fontSize: 12
}
}
]
}
}
})
// 监测点
getGridDiagramLineData({ ...form, statisticalType: loadType }).then(res => {
dotCharts.value = {
title: {
text: ''
},
// str.replace(/\(\d+\)/, "\n$&");
xAxis: {
data: res.data.map((item: any) => {
return item.orgName.length > 4
? item.orgName.slice(0, 4) + '\n ' + item.orgName.slice(4)
: item.orgName
}),
axisLabel: {
color: '#000',
fontSize: 12
}
},
yAxis: {
name: '',
min: 0,
max: 100
},
grid: {
top: '30px',
left: '0px',
right: '20px'
},
options: {
dataZoom: null,
series: [
{
name: '数据完整性',
type: 'bar',
data: res.data.map((item: any) => item.integrityRate),
label: {
show: true,
position: 'top',
fontSize: 12
}
}
]
}
}
})
}
defineExpose({ info })
</script>
<style lang="scss" scoped>
.boxLeft {
background-color: #fff;
width: 25%;
padding: 10px 0px 10px 10px;
font-size: 13px;
overflow: hidden;
}
.title {
// height: ;
display: flex;
justify-content: space-between;
font-size: 16px;
height: 22px;
line-height: 23px;
padding-left: 5px;
width: 100%;
background-image: linear-gradient(to right, #a4e5da, #fff);
.info {
font-weight: normal;
display: flex;
font-size: 12px;
cursor: pointer;
}
}
.evaluate {
height: 60px;
border: 1px solid #ccc;
margin: 10px 0;
padding: 10px 0;
display: flex;
justify-content: space-around;
text-align: center;
overflow-x: auto;
overflow-y: hidden;
}
.boxR {
border: 1px solid #ccc;
margin: 10px 0;
padding: 5px;
.num {
color: #2478f2;
}
.top {
display: flex;
justify-content: space-between;
}
.bottom {
margin: 0;
border: 0px;
}
}
:deep(.el-select) {
min-width: 120px;
}
.col {
display: flex;
flex-wrap: wrap;
justify-content: space-around;
align-items: center;
}
.imgR {
position: absolute;
padding: 10px;
top: calc(50% - 80px);
left: -23px;
z-index: 1;
height: 200px;
cursor: pointer;
}
:deep(.el-card) {
.el-card__header {
padding: 5px 20px;
font-size: 1.2rem;
&::before {
width: 0.3rem;
height: 1.2rem;
margin-top: 0.3rem;
position: absolute;
content: '';
background: var(--el-color-primary);
border-radius: 0.02rem;
left: 1.2rem;
}
}
.el-card__body {
padding: 10px;
}
margin-bottom: 10px;
margin-right: 10px;
}
</style>

View File

@@ -0,0 +1,487 @@
<template>
<div class="boxLeft" :style="height">
<!-- 指标合格率统计 -->
<el-card>
<template #header>
<div class="card-header" style="display: flex; justify-content: space-between">
<span>指标合格率统计</span>
<span class="info" @click="open(0)">
详情
<ArrowRight style="width: 12px" />
</span>
</div>
</template>
<MyEChart :style="`height:calc(${boxHeight.height} - 40px)`" :options="passingCharts" />
</el-card>
<el-card>
<template #header>
<div class="card-header">
<span>稳态指标超标占比</span>
</div>
</template>
<div>
<div class="monitoringPoints">
<div>在线监测点数{{ monitorList.onlineNum }}</div>
<div>超标监测点数{{ monitorList.overNum }}</div>
<div>超标监测点占比{{ monitorList.overRatio }}</div>
</div>
<MyEChart :style="boxHeight" :options="exceededCharts" />
</div>
</el-card>
<!-- 稳态电能质量指标水平评估 -->
<el-card>
<template #header>
<div class="card-header">
<span>稳态电能质量指标水平评估</span>
</div>
</template>
<div :style="`height:calc(${boxHeight.height} + 20px)`" class="boxSteps">
<el-segmented v-model="active" :options="Voltage" block @change="handleClick" />
<div class="evaluationData mt10">
<div v-for="(item, i) in evaluationData">
<el-row style="width: 100%">
<el-col :span="12" style="display: flex">
<img :src="url[i]" />
<span>{{ item.targetName }}</span>
</el-col>
<el-col :span="12" style="display: flex">
<div style="width: 50%">
均值
<span style="color: #339966">{{ item.avg == 3.14159 ? '--' : item.avg }}</span>
</div>
<div>
标准差
<span style="color: #ff9900">{{ item.avg == 3.14159 ? '--' : item.avg }}</span>
</div>
</el-col>
</el-row>
</div>
</div>
</div>
</el-card>
<!-- 暂态事件统计 -->
<el-card>
<template #header>
<div class="card-header">
<span>暂态事件统计</span>
</div>
</template>
<MyEChart :style="boxHeight" :options="statisticsCharts" />
</el-card>
<!-- 指标合格率统计 -->
<statistics ref="statisticsRef" />
</div>
</template>
<script setup lang="ts">
import { onMounted, reactive, ref, provide } from 'vue'
import statistics from '../components/city/statistics.vue'
import MyEChart from '@/components/echarts/MyEchart.vue'
import { useDictData } from '@/stores/dictData'
import { mainHeight } from '@/utils/layout'
import { color } from '@/components/echarts/color'
import { ArrowRight } from '@element-plus/icons-vue'
import { getAssessDetail, evaluationDetail, getGeneralSituation, getEvaluationData } from '@/api/device-boot/panorama'
const dictData = useDictData()
const height = mainHeight(20)
const boxHeight: any = mainHeight(280, 4)
const statisticsRef = ref()
const formRow: any = ref({})
const monitorList: any = ref({})
const statisticsCharts: any = ref({})
const rowList: any = ref({})
const chartRef = ref()
const evaluationData: any = ref([])
const passingCharts = ref()
const exceededCharts = ref()
const Voltage: any = dictData
.getBasicData('Dev_Voltage_Stand')
.filter(item => {
if (item.code == '35kV' || item.code == '500kV' || item.code == '220kV' || item.code == '110kV') {
return item
}
})
.map(item => {
return {
label: item.name,
value: item.id
}
})
const active: any = ref(Voltage[0].value)
const url: any = [
new URL(`@/assets/img/PLPC.png`, import.meta.url),
new URL(`@/assets/img/DYPC.png`, import.meta.url),
new URL(`@/assets/img/JBL.png`, import.meta.url),
new URL(`@/assets/img/SXDY.png`, import.meta.url),
new URL(`@/assets/img/SB.png`, import.meta.url)
]
const open = (e: number) => {
if (e == 0) {
statisticsRef.value.open(formRow.value)
}
}
const info = (row: any) => {
let form = {
...row,
id: row.orgNo,
deptIndex: row.orgNo,
orgId: row.orgNo,
ids: [],
statisticalType: dictData.getBasicData('Statistical_Type', ['Report_Type'])[0],
isUpToGrid: row.isUpToGrid,
monitorFlag: row.isUpToGrid
}
formRow.value = form
// 指标合格率统计
getAssessDetail(form).then(res => {
let data = [
{
name:
'电压偏差:' +
(res.data[0].vdevQualifyData == 3.14159 ? '暂无数据' : res.data[0].vdevQualifyData + '%'),
value: res.data[0].vdevQualifyData == 3.14159 ? '暂无数据' : res.data[0].vdevQualifyData
},
{
name:
'频率偏差:' +
(res.data[0].freqQualifyData == 3.14159 ? '暂无数据' : res.data[0].freqQualifyData + '%'),
value: res.data[0].freqQualifyData == 3.14159 ? '暂无数据' : res.data[0].freqQualifyData
},
{
name:
'电压总谐波畸变率:' +
(res.data[0].harmQualifyData == 3.14159 ? '暂无数据' : res.data[0].harmQualifyData + '%'),
value: res.data[0].harmQualifyData == 3.14159 ? '暂无数据' : res.data[0].harmQualifyData
},
{
name:
'闪变:' +
(res.data[0].flickerQualifyData == 3.14159 ? '暂无数据' : res.data[0].flickerQualifyData + '%'),
value: res.data[0].flickerQualifyData == 3.14159 ? '暂无数据' : res.data[0].flickerQualifyData
},
{
name:
'三相电压不平衡度:' +
(res.data[0].unbalanceQualifyData == 3.14159 ? '暂无数据' : res.data[0].unbalanceQualifyData + '%'),
value: res.data[0].unbalanceQualifyData == 3.14159 ? '暂无数据' : res.data[0].unbalanceQualifyData
}
]
let optionData = getData(data)
passingCharts.value = {
tooltip: {
show: true,
trigger: 'item',
formatter: '{b}'
},
xAxis: {
show: false
},
yAxis: {
show: false
},
legend: {
data: data.map((item: any) => item.name),
type: 'scroll',
orient: 'vertical',
icon: 'roundRect',
right: '10',
itemGap: 15,
itemWidth: 12,
itemHeight: 12,
textStyle: {
fontSize: '0.85rem'
}
},
options: {
dataZoom: null,
series: optionData.series
}
}
function getData(data: any) {
var res: any = {
series: [],
yAxis: []
}
for (let i = 0; i < data.length; i++) {
res.series.push({
type: 'pie',
clockWise: false, //顺时加载
hoverAnimation: false, //鼠标移入变大
radius: [70 - i * 15 + '%', 80 - i * 15 + '%'],
center: ['30%', '50%'],
label: {
show: false
},
data: [
{
value: data[i].value,
name: data[i].name
},
{
value: 100 - data[i].value,
name: '',
itemStyle: {
color: 'rgba(0,0,0,0)',
borderWidth: 0
},
tooltip: {
show: false
},
hoverAnimation: true
}
]
})
res.series.push({
name: '',
type: 'pie',
silent: true,
z: 1,
clockWise: false, //顺时加载
hoverAnimation: false, //鼠标移入变大
radius: [70 - i * 15 + '%', 80 - i * 15 + '%'],
center: ['30%', '50%'],
label: {
show: false
},
data: [
{
value: 7.5,
itemStyle: {
color: '#E3F0FF',
borderWidth: 0
},
tooltip: {
show: false
},
hoverAnimation: false
}
]
})
}
return res
}
})
// 稳态指标超标占比
evaluationDetail(form).then(res => {
monitorList.value = res.data[0]
exceededCharts.value = {
tooltip: {},
yAxis: {
name: '%',
type: 'value',
max: 100
},
legend: {
data: ['超标监测点数', '超标天数']
},
xAxis: {
type: 'category',
data: res.data[0].list.map((item: any) => {
return item.targetName.length > 4
? item.targetName.slice(0, 4) + '\n ' + item.targetName.slice(4)
: item.targetName
}),
axisLabel: {
color: '#000',
fontSize: 12
}
},
grid: {
top: '30px',
left: '10px',
right: '20px'
},
options: {
dataZoom: null,
series: [
{
name: '超标监测点数',
type: 'bar',
data: res.data[0].list.map((item: any) => item.overNum),
label: {
show: true,
position: 'top',
fontSize: 12,
formatter: function (params: any) {
return `${params.value == 3.14159 ? '' : params.value}`
}
}
},
{
name: '超标天数',
type: 'bar',
data: res.data[0].list.map((item: any) => item.overDay),
label: {
show: true,
position: 'top',
fontSize: 12,
formatter: function (params: any) {
return `${params.value == 3.14159 ? '' : params.value}`
}
}
}
]
}
}
})
// 稳态电能质量指标水平评估
handleClick(active.value)
// 暂态电能质量水平评估
getGeneralSituation({ ...form, monitorFlag: form.isUpToGrid == 0 ? 2 : 1 }).then(res => {
statisticsCharts.value = {
tooltip: {},
yAxis: {
type: 'value'
},
legend: {
show: false
},
xAxis: {
type: 'category',
data: ['电压暂升', '电压暂降', '短时中断']
},
grid: {
top: '30px',
left: '10px',
right: '20px'
},
options: {
dataZoom: null,
series: [
{
name: '暂态个数',
type: 'bar',
data: [res.data[0].upCount, res.data[0].sagsCount, res.data[0].breakCount],
label: {
show: true,
position: 'top',
fontSize: 12
}
}
]
}
}
})
}
// 点击电压等级
const handleClick = (i: any) => {
active.value = i
getEvaluationData({
...formRow.value,
voltageLevel: i
}).then(res => {
evaluationData.value = res.data
})
}
defineExpose({ info })
</script>
<style lang="scss" scoped>
.boxLeft {
background-color: #fff;
width: 25%;
padding: 10px 0px 10px 10px;
font-size: 13px;
overflow: hidden;
}
.info {
font-weight: normal;
display: flex;
font-size: 12px;
cursor: pointer;
align-items: center;
}
.evaluate {
height: 60px;
border: 1px solid #ccc;
margin: 10px 0;
padding: 10px 0;
display: flex;
justify-content: space-around;
text-align: center;
overflow-x: auto;
overflow-y: hidden;
}
.boxR {
border: 1px solid #ccc;
margin: 10px 0;
padding: 5px;
.num {
color: #2478f2;
}
.top {
display: flex;
justify-content: space-between;
}
.bottom {
margin: 0;
border: 0px;
}
}
:deep(.el-card) {
.el-card__header {
padding: 5px 20px;
font-size: 1.2rem;
&::before {
width: 0.3rem;
height: 1.2rem;
margin-top: 0.3rem;
position: absolute;
content: '';
background: var(--el-color-primary);
border-radius: 0.02rem;
left: 1.2rem;
}
}
.el-card__body {
padding: 10px;
}
margin-bottom: 10px;
margin-right: 10px;
}
.monitoringPoints {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
text-align: center;
}
:deep(.boxSteps) {
font-size: 1rem;
.evaluationData {
display: grid;
height: calc(100% - 30px);
grid-template-rows: repeat(5, auto);
}
img {
width: 1.3rem;
height: 1.3rem;
margin-right: 10px;
}
}
:deep(.el-segmented) {
--el-border-radius-base: 16px;
}
</style>

View File

@@ -39,7 +39,7 @@
import { ref } from 'vue'
import MyEChart from '@/components/echarts/MyEchart.vue'
import { defaultAttribute } from '@/components/table/defaultAttribute'
import { getAssessDetail } from '@/api/device-boot/panorama'
import { getAssessDetail, getAssessTrend } from '@/api/device-boot/panorama'
const dialogVisible: any = ref(false)
const tableData: any = ref([])
@@ -48,50 +48,54 @@ const picEChart = ref()
const open = async (row: any) => {
getAssessDetail(row).then(res => {
tableData.value = res.data
})
getAssessTrend(row).then(res => {
picEChart.value = {
tooltip: {
formatter: function (params: any) {
let tips = ''
if (params[0].value == 3.14159) {
tips += params[0].name + ':暂无数据<br/>'
} else {
tips += params[0].name + ':' + params[0].value + '%<br/>'
}
return tips
}
},
title: {
text: ''
text: '各地市综合评估趋势对比'
},
xAxis: {
name: '(区域)',
data: res.data.map((item: any) => item.deptName)
},
grid: {
bottom: '10px'
},
yAxis: {
name: '',
min: 0,
max: 100
name: ''
},
options: {
series: [
{
name: '评估得分',
type: 'bar',
dataZoom: false,
series: []
}
}
data: res.data.map((item: any) => item.assessData)
}
]
}
let list: any = []
let time: any = []
res.data.forEach((item: any, num: any) => {
time = []
item.children.forEach((val: any, i: any) => {
if (num == 0) {
list.push([])
}
time.push(val.dataTime)
list[i].push(val.score == 3.14159 ? null : val.score)
})
})
list.forEach((item: any, i: any) => {
picEChart.value.options.series.push({
name: time[i],
type: 'line',
data: item
})
})
})
dialogVisible.value = true
}
const formatter = (row: any) => {
if (row.cellValue == 3.14159) {
return '暂无数据'
return '/'
} else {
return row.cellValue
}

View File

@@ -17,7 +17,7 @@
<span>分布统计</span>
<el-select
v-model="statisticalType"
:value-key='id'
value-key="id"
style="width: 120px; margin-right: 80px"
@change="statiStics"
>

View File

@@ -14,15 +14,37 @@
</el-col>
<el-col :span="12">
<div class="title">
<span>污染告警</span>
<span>
污染告警
<el-popover placement="right" :width="150" trigger="hover">
<template #reference>
<WarningFilled class="WarningFilled" />
</template>
<div class="text">
<span style="color: #00b07d">无污染(0,1]</span>
<br />
<span style="color: #3399ff">轻微污染(1,1.2]</span>
<br />
<span style="color: #ffcc33">轻度污染(1.2,1.6]</span>
<br />
<span style="color: #ff9900">中度污染(1.6,2]</span>
<br />
<span style="color: #cc0000">重度污染(2,+)</span>
</div>
</el-popover>
</span>
<el-select v-model="contaminate" style="width: 120px; margin-right: 80px" @change="contaminateC">
<el-option v-for="item in options" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</div>
<div class="pie">
<MyEChart v-for="item in picEChart" class="MyEChart" :options="item" />
</div>
</el-col>
</el-row>
<div>
<div class="title">
<div class="title mb10">
<span>变电站详细列表</span>
</div>
<vxe-table v-bind="defaultAttribute" ref="vxeRef" height="300px" :data="tableData">
@@ -40,11 +62,19 @@ import { ref } from 'vue'
import MyEChart from '@/components/echarts/MyEchart.vue'
import { useDictData } from '@/stores/dictData'
import { defaultAttribute } from '@/components/table/defaultAttribute'
import { WarningFilled } from '@element-plus/icons-vue'
import { getPollutionAlarmPageData, getPollutionAlarmData, getGridDiagramSubTendency } from '@/api/device-boot/panorama'
const dictData = useDictData()
const dialogVisible: any = ref(false)
const time = ref('1')
const Voltage = dictData.getBasicData('Dev_Voltage_Stand')
const options: any = dictData.getBasicData('Pollution_Statis').filter(item => {
if (item.code == 'V_Harmonic' || item.code == 'I_All') {
return item
}
})
const contaminate = ref(options[0].id)
const rowList: any = ref({})
const trendEChart: any = ref({})
const tableData: any = ref([])
@@ -62,7 +92,16 @@ const open = async (row: any) => {
}
analysis(1)
// 污染
getPollutionAlarmData(rowList.value).then(res => {
contaminateC()
// 列表
getPollutionAlarmPageData(rowList.value).then(res => {
tableData.value = res.data.records
})
dialogVisible.value = true
}
const contaminateC = () => {
getPollutionAlarmData({ ...rowList.value, ids: [contaminate.value] }).then(res => {
let data = []
let a1 = 0
@@ -70,7 +109,7 @@ const open = async (row: any) => {
let a3 = 0
let a4 = 0
let a5 = 0
if (row.isUpToGrid == 0) {
if (rowList.value.isUpToGrid == 0) {
data = res.data.info
} else {
data = res.data.gwInfo
@@ -111,7 +150,7 @@ const open = async (row: any) => {
name: `重度污染:${a5}`
}
]
const color = ['#00B07D', '#FFAF00', '#FF7D00', '#B90000', '#62298B']
const color = ['#00B07D', '#3399ff', '#ffcc33', '#ff9900', '#cc0000']
list.forEach((item, i) => {
picEChart.value[i] = {
@@ -208,15 +247,9 @@ const open = async (row: any) => {
}
})
})
// 列表
getPollutionAlarmPageData(rowList.value).then(res => {
tableData.value = res.data.records
})
dialogVisible.value = true
}
const analysis = (e: any) => {
let time = rowList.value.searchBeginTime.slice(0, 4) + `-01-01`
let time = rowList.value.searchBeginTime?.slice(0, 4) + `-01-01`
// 分析
getGridDiagramSubTendency({ ...rowList.value, searchBeginTime: time, type: e }).then(res => {
let name = []
@@ -262,9 +295,9 @@ const analysis = (e: any) => {
}
const formatter = (row: any) => {
if (row.column.field == 'dataV') {
return row.cellValue == 3.14159 ? '暂无数据' : row.cellValue
return row.cellValue == 3.14159 ? '/' : row.cellValue
} else if (row.column.field == 'data') {
return row.cellValue == 3.14159 ? '暂无数据' : row.cellValue
return row.cellValue == 3.14159 ? '/' : row.cellValue
} else if (row.column.field == 'voltageLevel') {
return Voltage.filter((item: any) => item.id == row.cellValue)[0]?.name
} else {
@@ -276,7 +309,6 @@ const handleClose = () => {
dialogVisible.value = false
}
defineExpose({ open })
</script>
<style lang="scss" scoped>
@@ -286,10 +318,15 @@ defineExpose({ open })
.title {
display: flex;
justify-content: space-between;
position: relative;
span {
font-weight: 550;
font-size: 18px;
}
.WarningFilled {
width: 16px;
cursor: pointer;
}
}
.pie {
display: flex;

View File

@@ -57,16 +57,16 @@
<vxe-column field="overRatio" title="超标监测点占比(%)" />
<vxe-colgroup :title="item" v-for="(item, i) in title">
<vxe-column title="超标天数">
<template #default="scope">
<span>{{ scope.row.list[i].overDay }}</span>
</template>
</vxe-column>
<vxe-column title="超标监测点数 ">
<template #default="scope">
<span>{{ scope.row.list[i].overNum }}</span>
</template>
</vxe-column>
<vxe-column title="超标天数">
<template #default="scope">
<span>{{ scope.row.list[i].overDay }}</span>
</template>
</vxe-column>
</vxe-colgroup>
</vxe-table>
</div>
@@ -204,23 +204,6 @@ const echart = (row: any) => {
})
})
row.forEach((item: any, i: any) => {
option.series.push({
name: item.time + 1,
type: 'radar',
symbol: 'none',
areaStyle: {
normal: {
color: color[i + 1]
}
},
itemStyle: {
color: color[i + 1]
},
data: [item.ratioList]
})
})
chart.setOption(option)
}

View File

@@ -1,6 +1,6 @@
<!-- 暂态 -->
<template>
<el-dialog draggable title="暂态电能质量水平评详情" v-model="dialogVisible" width="1400px">
<el-dialog draggable title="暂态电能质量水平评详情" v-model="dialogVisible" width="1400px">
<div>
<vxe-table v-bind="defaultAttribute" ref="vxeRef" height="300px" :data="tableData">
<vxe-column field="devName" title="所属区域" />

View File

@@ -17,8 +17,8 @@
<span>分布统计</span>
</div>
<div class="pie">
<MyEChart style="height: 260px; width: 60%" :options="picEChart" />
<el-table size="small" height="260px" style="width: 35%" :data="picList">
<MyEChart style="height: 260px; width: 50%" :options="picEChart" />
<el-table size="small" height="260px" style="width: 50%" :data="picList">
<el-table-column prop="orgName" width="80px" align="center"></el-table-column>
<el-table-column prop="onlineEvaluate" label="终端评价" align="center">
<template #default="scope">
@@ -42,7 +42,8 @@
</span>
</template>
</el-table-column>
<el-table-column prop="devCount" label="数" align="center"></el-table-column>
<el-table-column prop="devCount" label="在运终端数" align="center" />
<el-table-column prop="devCount" label="在线终端数" align="center" />
</el-table>
</div>
</el-col>
@@ -141,12 +142,12 @@ const open = async (row: any) => {
endAngle: 0,
labelLine: {
length: 8,
length2: 50,
length2: 30,
show: true
},
label: {
padding: [0, -50],
formatter: '{b}:{c}台\n\n'
padding: [0, -30],
formatter: '{b}\n\n'
},
itemStyle: {
borderColor: '#fff',
@@ -220,7 +221,7 @@ const analysis = (e: any) => {
}
const formatter = (row: any) => {
if (row.column.field == 'onlineEvaluate') {
return row.cellValue == 3.14159 ? '暂无数据' : row.cellValue * 100
return row.cellValue == 3.14159 ? '/' : row.cellValue * 100
} else {
return row.cellValue
}

View File

@@ -239,12 +239,12 @@
</div>
</div> -->
</div>
<img
<!-- <img
class="imgL"
:style="show ? 'transform: rotate(0deg);' : 'transform: rotate(180deg);'"
@click="show = !show"
src="@/assets/img/QH.png"
/>
/> -->
<!-- 变电站详情 -->
<stand ref="standRef" />
@@ -277,24 +277,24 @@ const list: any = ref([
titleT: ['总数', '告警'],
list: [
{
numOne: 1,
numTwo: 1
numOne: 0,
numTwo: 0
},
{
numOne: 2,
numTwo: 2
numOne: 0,
numTwo: 0
},
{
numOne: 3,
numTwo: 3
numOne: 0,
numTwo: 0
},
{
numOne: 4,
numTwo: 4
numOne: 0,
numTwo: 0
},
{
numOne: 5,
numTwo: 5
numOne: 0,
numTwo: 0
}
]
},
@@ -304,24 +304,24 @@ const list: any = ref([
titleT: ['终端个数', '终端在线率'],
list: [
{
numOne: 1,
numTwo: 1
numOne: 0,
numTwo: 0
},
{
numOne: 2,
numTwo: 2
numOne: 0,
numTwo: 0
},
{
numOne: 3,
numTwo: 3
numOne: 0,
numTwo: 0
},
{
numOne: 4,
numTwo: 4
numOne: 0,
numTwo: 0
},
{
numOne: 5,
numTwo: 5
numOne: 0,
numTwo: 0
}
]
},
@@ -331,24 +331,24 @@ const list: any = ref([
titleT: ['总数', '在线'],
list: [
{
numOne: 1,
numTwo: 1
numOne: 0,
numTwo: 0
},
{
numOne: 2,
numTwo: 2
numOne: 0,
numTwo: 0
},
{
numOne: 3,
numTwo: 3
numOne: 0,
numTwo: 0
},
{
numOne: 4,
numTwo: 4
numOne: 0,
numTwo: 0
},
{
numOne: 5,
numTwo: 5
numOne: 0,
numTwo: 0
}
]
}
@@ -379,9 +379,27 @@ const info = (row: any) => {
monitorFlag: row.isUpToGrid
}
formRow.value = form
getSubLineGiveAnAlarm(form).then()
getGridDiagramDev(form)
getGridDiagramMonitor(form)
// 变电站
getSubLineGiveAnAlarm(form).then(res => {
let data = row.isUpToGrid == 1 ? res.data.gwInfo : res.data.info
list.value[0].list = data[0].data
})
// 终端
getGridDiagramDev(form).then(res => {
let data = row.isUpToGrid == 1 ? res.data.gwInfo : res.data.info
list.value[1].list = data[0].data
})
// 监测点
getGridDiagramMonitor(form).then(res => {
let data = row.isUpToGrid == 1 ? res.data.gwInfo : res.data.info
list.value[2].list = data[0].data.map((item: any) => {
return {
numOne: item.num,
numTwo: item.onLineNum
}
})
})
}
onMounted(() => {})
defineExpose({ info })
@@ -433,7 +451,6 @@ defineExpose({ info })
font-size: 12px;
cursor: pointer;
}
}
.infoTop {
display: flex;

View File

@@ -66,10 +66,10 @@
<MyEChart :style="EchHeight" :options="harmonicCharts" />
</div>
</div>
<!-- 暂态电能质量水平评 -->
<!-- 暂态电能质量水平评 -->
<div>
<div class="title">
<span>暂态电能质量水平评</span>
<span>暂态电能质量水平评</span>
<span class="info" @click="open(2)">
详情
<ArrowRight style="width: 12px" />
@@ -137,12 +137,12 @@
</div>
</div>
</div>
<img
<!-- <img
class="imgR"
:style="show ? 'transform: rotate(180deg);' : 'transform: rotate(0deg);'"
@click="show = !show"
src="@/assets/img/QH.png"
/>
/> -->
<!-- 评估 -->
<Evaluate ref="evaluateRef" />
<!-- 稳态 -->
@@ -259,7 +259,7 @@ const info = (row: any) => {
getAssessOverview(form).then(res => {
assessList.value = res.data
})
// 稳态电能质量水平评概览数据
// 稳态电能质量水平评概览数据
harmonicChange()
// 暂态统计详情
transientChange()
@@ -433,10 +433,9 @@ defineExpose({ info })
min-width: 120px;
}
.col {
display: flex;
flex-wrap: wrap;
justify-content: space-around;
align-items: center;
display: grid;
grid-template-columns: 1fr;
text-align: center;
}
.imgR {
position: absolute;

View File

@@ -1,6 +1,6 @@
<template>
<div class="default-main" :style="height">
<div class="box">
<div class="box" >
<DatePicker ref="datePickerRef" style="display: none" />
<el-form :inline="true" :model="form" class="demo-form-inline">
<el-form-item>
@@ -16,7 +16,7 @@
:show-all-levels="false"
v-model="form.orgNo"
style="width: 100px"
@changeName="changeName"
@changeValue="changeValue"
/>
</el-form-item>
<el-form-item>
@@ -33,12 +33,18 @@
<!-- 地图 -->
<!-- <Map /> -->
<!-- 左边 -->
<!-- 省级 -->
<div v-show="control == 1">
<mapL ref="mapLRef" class="mapL" />
<!-- 右边 -->
<mapR ref="mapRRef" class="mapR" />
</div>
<!-- 市级 -->
<div v-show="control == 2">
<cityMapL ref="cityMapLRef" class="mapL" />
<cityMapB ref="cityMapBRef" class="mapB" />
<cityMapR ref="cityMapRRef" class="mapR" />
</div>
</div>
</template>
<script setup lang="ts">
@@ -49,6 +55,9 @@ import { useDictData } from '@/stores/dictData'
import { mainHeight } from '@/utils/layout'
import { Search, Refresh } from '@element-plus/icons-vue'
import mapL from './components/mapL.vue'
import cityMapL from './components/cityMapL.vue'
import cityMapR from './components/cityMapR.vue'
import cityMapB from './components/cityMapB.vue'
import mapR from './components/mapR.vue'
import DatePicker from '@/components/form/datePicker/index.vue'
const dictData = useDictData()
@@ -59,6 +68,8 @@ const datePickerRef = ref()
const areaRef = ref()
const mapLRef = ref()
const mapRRef = ref()
const cityMapLRef = ref()
const cityMapRRef = ref()
const options: any = ref([
{
name: dictData.state.area[0].name,
@@ -69,6 +80,7 @@ const options: any = ref([
id: 1
}
])
const control = ref(1)
const form: any = ref({
name: '',
orgNo: dictData.state.area[0].id,
@@ -76,17 +88,24 @@ const form: any = ref({
})
const height = mainHeight(10)
const changeName = (e: any) => {
options.value[0].name = e
// 获取区域名称
const changeValue = (e: any) => {
options.value[0].name = e.label
if (e.level == 1) {
control.value = 1
} else {
control.value = 2
}
info()
}
const reset = () => {
form.value = {
name: '',
orgNo: dictData.state.area[0].id,
isUpToGrid: 0
}
control.value = 1
info()
}
const info = () => {
@@ -99,9 +118,13 @@ const info = () => {
form.value.endTime = `2024-07-30`
form.value.searchEndTime = `2024-07-30`
form.value.type = datePickerRef.value.interval
if (control.value == 1) {
mapLRef.value.info(form.value)
mapRRef.value.info(form.value)
} else {
cityMapRRef.value.info(form.value)
cityMapLRef.value.info(form.value)
}
}
onMounted(() => {
info()
@@ -131,6 +154,11 @@ onMounted(() => {
top: 10px;
right: 10px;
}
.mapB {
position: absolute;
bottom: 0px;
left: 25%;
}
.default-main {
margin: 10px 0 0 0;
}