统计分析

This commit is contained in:
geting 2024-11-27 09:47:29 +08:00
parent 0c96a07849
commit cc7fc7fc64
4 changed files with 257 additions and 199 deletions

View File

@ -13,9 +13,16 @@
</template> </template>
</el-popover> </el-popover>
</el-menu> </el-menu>
<keep-alive>
<PowerCurveAnalysis v-if="activeIndex == 0"></PowerCurveAnalysis> <PowerCurveAnalysis v-if="activeIndex == 0"></PowerCurveAnalysis>
</keep-alive>
<keep-alive>
<TrendAnalysis v-if="activeIndex == 1"></TrendAnalysis> <TrendAnalysis v-if="activeIndex == 1"></TrendAnalysis>
</keep-alive>
<keep-alive>
<TrendComparison v-if="activeIndex == 2"></TrendComparison> <TrendComparison v-if="activeIndex == 2"></TrendComparison>
</keep-alive>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">

View File

@ -54,7 +54,10 @@
</div> </div>
</div> </div>
</div> </div>
<div ref="chartContainer" style="position: absolute; bottom: 0px; width: 100%; height: 400px; border: 1px solid rgb(217, 217, 217)"></div> <div
ref="chartContainer"
style="height: calc(100% - 240px); width: calc(100% - 60px); border: 1px solid rgb(217, 217, 217); margin: 30px"
></div>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
@ -124,10 +127,7 @@ const option: any = {
}, },
}, },
series: [], series: [],
grid: { grid: {},
left: '3%',
right: '3%',
},
} }
const chart: any = ref(null) const chart: any = ref(null)
@ -148,10 +148,7 @@ onMounted(() => {
type: 'line', type: 'line',
}, },
], ],
grid: { grid: {},
left: '3%',
right: '3%',
},
}) })
chart.value.on('legendselectchanged', function (event: any) { chart.value.on('legendselectchanged', function (event: any) {
var isSelected = event.selected[event.name] var isSelected = event.selected[event.name]
@ -264,6 +261,14 @@ const getDateRange = (type: 'week' | 'month') => {
const isLoading = ref(false) const isLoading = ref(false)
const statAnalysisOperate = () => { const statAnalysisOperate = () => {
const deviceId = statAnalysisDeviceId.value.split(':')[2]
if (!deviceId) {
ElMessage.info('请选择风机!')
return
} else if (!statAnalysisTime.value.length) {
ElMessage.info('请选择查询时间!')
return
}
isLoading.value = true isLoading.value = true
option.series = [] option.series = []
option.legend.data = [] option.legend.data = []
@ -271,7 +276,7 @@ const statAnalysisOperate = () => {
const requestData = { const requestData = {
devices: [ devices: [
{ {
deviceId: statAnalysisDeviceId.value.split(':')[2], deviceId: deviceId,
attributes: ['iGenPower', 'iWindSpeed'], attributes: ['iGenPower', 'iWindSpeed'],
}, },
], ],
@ -392,6 +397,9 @@ const statAnalysisExport = () => {
<style scoped lang="scss"> <style scoped lang="scss">
.statAnalysis { .statAnalysis {
height: 100%; height: 100%;
.contain {
height: calc(100% - 60px);
}
.headerPart { .headerPart {
padding: 20px; padding: 20px;
display: flex; display: flex;

View File

@ -1,6 +1,7 @@
<template> <template>
<div class="contain"> <div class="contain">
<el-header class="headerPart"> <div class="left">
<div class="headerPart">
<div class="topLeft"> <div class="topLeft">
<div class="selectPart"> <div class="selectPart">
<span>{{ t('statAnalysis.deviceId') }}</span> <span>{{ t('statAnalysis.deviceId') }}</span>
@ -24,21 +25,33 @@
</div> </div>
<div class="selectPart"> <div class="selectPart">
<span>{{ t('statAnalysis.interval') }}</span> <span>{{ t('statAnalysis.interval') }}</span>
<el-select v-model="statAnalysisSelect.interval" :placeholder="'请选择' + t('statAnalysis.interval')" class="statAnalysisSelect"> <el-select
v-model="statAnalysisSelect.interval"
:placeholder="'请选择' + t('statAnalysis.interval')"
class="statAnalysisSelect"
>
<el-option v-for="v in statAnalysisSelectOptions.interval" :key="v.value" :label="v.label" :value="v.value"></el-option> <el-option v-for="v in statAnalysisSelectOptions.interval" :key="v.value" :label="v.label" :value="v.value"></el-option>
</el-select> </el-select>
</div> </div>
</div> </div>
<div class="topRight"> <div class="topRight">
<el-button type="primary" :icon="Plus" @click="addTime()"> 增加</el-button>
<el-button type="primary" :loading="isLoading" @click="statAnalysisOperate()">{{ t('statAnalysis.search') }}</el-button> <el-button type="primary" :loading="isLoading" @click="statAnalysisOperate()">{{ t('statAnalysis.search') }}</el-button>
<el-button style="color: #0064aa" @click="statAnalysisExport()">{{ t('statAnalysis.export') }}</el-button> <el-button style="color: #0064aa" @click="statAnalysisExport()">{{ t('statAnalysis.export') }}</el-button>
</div> </div>
</el-header> </div>
<div class="timeColumns"> <div class="timeColumns">
<div class="headerPart" v-for="(time, index) in times" :key="index"> <div class="moduleRow" v-for="(time, index) in times" :key="index">
<div class="topLeft"> <div class="item">
<el-icon v-show="index !== 0" class="removeModule" @click="switchTime(index)">
<Close />
</el-icon>
<div class="selectPart"> <div class="selectPart">
<span>曲线名称</span>
<el-input v-model="customName[index]" class="customName"></el-input> <el-input v-model="customName[index]" class="customName"></el-input>
</div>
<div class="selectPart">
<span>{{ t('statAnalysis.time') }}</span> <span>{{ t('statAnalysis.time') }}</span>
<el-date-picker <el-date-picker
class="datetime-picker" class="datetime-picker"
@ -52,13 +65,6 @@
/> />
</div> </div>
<div class="topLeft"> <div class="topLeft">
<div class="icon">
<el-button v-show="index === times.length - 1" type="primary" size="small" :icon="Plus" circle @click="addTime(index)">
</el-button>
</div>
<div class="icon">
<el-button v-show="index !== 0" type="danger" size="small" :icon="Delete" @click="switchTime(index)" circle></el-button>
</div>
<div class="selectPart" v-if="calculate[index] && calculate[index]['max'] !== ''"> <div class="selectPart" v-if="calculate[index] && calculate[index]['max'] !== ''">
<span>{{ t('statAnalysis.max') }}</span> <span>{{ t('statAnalysis.max') }}</span>
<span class="max">{{ calculate[index]['max'] }}</span> <span class="max">{{ calculate[index]['max'] }}</span>
@ -75,7 +81,14 @@
</div> </div>
</div> </div>
</div> </div>
<div ref="chartContainer" style="position: absolute; bottom: 0px; width: 100%; height: 400px; border: 1px solid rgb(217, 217, 217)"></div> </div>
<div class="right">
<div
ref="chartContainer"
style="position: absolute; top: 127px; width: calc(100% - 430px); height: 400px; border: 1px solid rgb(217, 217, 217)"
></div>
</div>
</div>
<el-dialog v-model="showMeasure" title="测点名称" :width="800"> <el-dialog v-model="showMeasure" title="测点名称" :width="800">
<template #header> <template #header>
<div class="measureSlotHeader"> <div class="measureSlotHeader">
@ -92,14 +105,13 @@
</span> </span>
</template> </template>
</el-dialog> </el-dialog>
</div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { reactive, ref, onMounted, markRaw } from 'vue' import { reactive, ref, onMounted, markRaw } from 'vue'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import { queryWindTurbinesPages, historyReq, trendAnalyseExport } from '/@/api/backend/statAnalysis/request' import { queryWindTurbinesPages, historyReq, trendAnalyseExport } from '/@/api/backend/statAnalysis/request'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import { DArrowRight, Plus, Delete } from '@element-plus/icons-vue' import { DArrowRight, Plus, Delete, Close } from '@element-plus/icons-vue'
import MeasurementPage from './analysisAttributes.vue' import MeasurementPage from './analysisAttributes.vue'
import * as echarts from 'echarts' import * as echarts from 'echarts'
const { t } = useI18n() const { t } = useI18n()
@ -111,9 +123,9 @@ const statAnalysisSelect = reactive({
time: '', time: '',
}) })
const times: any = reactive([{ time: '' }]) const times: any = reactive([{ time: '' }])
const addTime = (index: any) => { const addTime = () => {
times.push({ time: '' }) times.push({ time: '' })
customName.push(statAnalysisSelect.attributes + String(index + 2)) customName.push(statAnalysisSelect.attributes + String(times.length))
} }
const switchTime = (index: number) => { const switchTime = (index: number) => {
times.splice(index, 1) times.splice(index, 1)
@ -186,10 +198,7 @@ const option: any = {
type: 'value', type: 'value',
}, },
series: [], series: [],
grid: { grid: {},
left: '3%',
right: '3%',
},
} }
const statAnalysisSelectOptions: any = reactive({ const statAnalysisSelectOptions: any = reactive({
@ -220,10 +229,7 @@ onMounted(() => {
type: 'line', type: 'line',
}, },
], ],
grid: { grid: {},
left: '3%',
right: '3%',
},
}) })
} }
queryWindTurbines() queryWindTurbines()
@ -343,6 +349,19 @@ const calculate: any = ref([{ max: '', min: '', average: '' }])
var xDatas: any = [] var xDatas: any = []
const isLoading = ref(false) const isLoading = ref(false)
const statAnalysisOperate = () => { const statAnalysisOperate = () => {
const findTime = times.filter((item: any) => {
return Array.isArray(item)
})
if (!statAnalysisSelect.deviceId) {
ElMessage.info('请选择风机!')
return
} else if (!statAnalysisSelect.attributeCode) {
ElMessage.info('请选择测点名称!')
return
} else if (!findTime.length) {
ElMessage.info('请选择查询时间!')
return
}
isLoading.value = true isLoading.value = true
option.series = [] option.series = []
option.legend.data = [] option.legend.data = []
@ -389,9 +408,7 @@ const historyDataReq = (promises: any) => {
const deviceId = statAnalysisSelect.deviceId const deviceId = statAnalysisSelect.deviceId
const attributeCode = statAnalysisSelect.attributeCode const attributeCode = statAnalysisSelect.attributeCode
results.forEach((res: any, index: number) => { results.forEach((res: any, index: number) => {
console.log(times[index])
const resData = (res && deviceId in res && res[deviceId][attributeCode]) || undefined const resData = (res && deviceId in res && res[deviceId][attributeCode]) || undefined
if (!resData['values'].length) { if (!resData['values'].length) {
ElMessage.info(`${customName[index]}数据为空`) ElMessage.info(`${customName[index]}数据为空`)
return return
@ -401,14 +418,9 @@ const historyDataReq = (promises: any) => {
new Date(times[index][1]).getTime(), new Date(times[index][1]).getTime(),
statAnalysisSelect.interval || '5m' statAnalysisSelect.interval || '5m'
) )
console.log(alltimes)
console.log('🚀 ~ results.forEach ~ resData:', resData)
const fillData = fillMissingData(alltimes, resData) const fillData = fillMissingData(alltimes, resData)
console.log('🚀 ~ results.forEach ~ fillData:', fillData)
const xData = fillData['times'] const xData = fillData['times']
const yData = fillData['values'] const yData = fillData['values']
console.log(xData)
calculate.value[index] = calculateStats(resData['values']) calculate.value[index] = calculateStats(resData['values'])
xDatas.push({ xDatas.push({
series: String(customName[index]), series: String(customName[index]),
@ -552,25 +564,79 @@ const timestampToTime = (timestamp: any) => {
height: 100%; height: 100%;
.contain { .contain {
height: calc(100% - 60px); height: calc(100% - 60px);
display: flex;
.right {
width: calc(100% - 418px);
}
} }
.headerPart { .headerPart {
padding: 20px; padding: 20px;
display: flex;
justify-content: space-between;
align-items: center;
.topLeft { .topLeft {
display: flex;
.icon { .icon {
width: 40px; width: 40px;
height: 40px; height: 40px;
padding: 10px 0; padding: 10px 0;
} }
.dialog-footer button:first-child {
margin-right: 10px;
}
}
.topRight {
display: flex;
margin: 12px 0;
.el-button {
width: 100px;
height: 40px;
}
}
}
.timeColumns {
.moduleRow {
.item {
position: relative;
margin: 10px 8px;
width: 402px;
min-height: 170px;
padding: 20px 5px 30px 5px;
border-radius: 20px;
box-shadow: 0px 0px 12px rgba(0, 0, 0, 0.12);
.topLeft {
display: flex;
}
.selectPart {
width: 370px;
justify-content: left;
span {
white-space: nowrap;
}
}
}
}
.removeModule {
position: absolute;
top: 2px;
right: 5px;
color: rgba(0, 0, 0, 0);
cursor: pointer;
transition: color 0.5s;
}
&:hover {
.removeModule {
color: rgba(0, 0, 0, 1);
}
}
.headerPart {
padding: 2px 20px;
}
}
.selectPart { .selectPart {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
height: 40px; height: 40px;
margin-right: 20px; margin-right: 20px;
width: 340px;
margin: 4px 0;
span { span {
margin-right: 10px; margin-right: 10px;
} }
@ -608,29 +674,7 @@ const timestampToTime = (timestamp: any) => {
color: #0064aa; color: #0064aa;
} }
.customName { .customName {
width: 120px; width: 360px;
margin-right: 10px;
}
}
.dialog-footer button:first-child {
margin-right: 10px;
}
}
.topRight {
// width: 436px;
display: flex;
justify-content: space-between;
.el-button {
width: 100px;
height: 40px;
}
}
}
.timeColumns {
height: calc(100% - 480px);
overflow-y: auto;
.headerPart {
padding: 2px 20px;
} }
} }
#myEChart { #myEChart {

View File

@ -199,10 +199,7 @@ const option: any = {
type: 'value', type: 'value',
}, },
series: [], series: [],
grid: { grid: {},
left: '3%',
right: '3%',
},
} }
const customName = reactive(['1']) const customName = reactive(['1'])
@ -222,10 +219,7 @@ onMounted(() => {
type: 'line', type: 'line',
}, },
], ],
grid: { grid: {},
left: '3%',
right: '3%',
},
}) })
} }
queryWindTurbines() queryWindTurbines()
@ -487,6 +481,7 @@ const timestampToTime = (timestamp: any) => {
height: 38px; height: 38px;
} }
} }
.calc {
.max, .max,
.min, .min,
.average { .average {
@ -512,6 +507,7 @@ const timestampToTime = (timestamp: any) => {
color: #0064aa; color: #0064aa;
} }
} }
}
.dialog-footer button:first-child { .dialog-footer button:first-child {
margin-right: 10px; margin-right: 10px;
} }
@ -532,6 +528,9 @@ const timestampToTime = (timestamp: any) => {
.headerPart { .headerPart {
padding: 2px 20px; padding: 2px 20px;
} }
.topLeft {
display: flex !important;
}
} }
#myEChart { #myEChart {
width: 100%; width: 100%;