From 1784c7bde96755daf0782e219878083671e0e116 Mon Sep 17 00:00:00 2001 From: dengzedong <dengzedong@email> Date: 星期一, 21 四月 2025 09:34:52 +0800 Subject: [PATCH] 数据分析 默认影响时间和影响因素 --- src/views/model/pre/analysis/index.vue | 541 +++++++++++++++++++++++++++++++++++++++++++++++++---- 1 files changed, 495 insertions(+), 46 deletions(-) diff --git a/src/views/model/pre/analysis/index.vue b/src/views/model/pre/analysis/index.vue index 5c75f02..e377c25 100644 --- a/src/views/model/pre/analysis/index.vue +++ b/src/views/model/pre/analysis/index.vue @@ -69,6 +69,7 @@ node-key="id" ref="treeRef" highlight-current + check-strictly :filter-node-method="filterNode" @check="onCheckTree"/> </div> @@ -78,33 +79,23 @@ <el-form :inline="true" :model="calRateForm" :rules="formRules" ref="calRateFormRef" label-width="108px"> <el-row> - <el-col :span="6"> - <el-form-item label="预测项" prop="calItem" style="width: 90%"> - <el-select size="small" v-model="calRateForm.calItem" - @change="calItemBaseVale" - placeholder="请选择"> - <el-option - v-for="itemOut in formData.checkedItemData" - :key="itemOut.id" - :label="itemOut.label" - :value="itemOut.id"/> - </el-select> - </el-form-item> - </el-col> - <el-col :span="6"> +<!-- <el-col :span="6" style="display: flex;align-items: center;justify-content: center">--> +<!-- <span>预测项:{{formData.checkedItemData?.label || ''}}</span>--> +<!-- </el-col>--> + <el-col :span="8"> <el-form-item label="精准度偏差" prop="IN_DEVIATION"> <el-input-number size="small" v-model="calRateForm.IN_DEVIATION" controls-position="right" :min="0"/> </el-form-item> </el-col> - <el-col :span="6"> + <el-col :span="8"> <el-form-item label="不可信率偏差" prop="OUT_DEVIATION"> <el-input-number size="small" v-model="calRateForm.OUT_DEVIATION" controls-position="right" :min="1"/> </el-form-item> </el-col> - <el-col :span="4"> + <el-col :span="8"> <el-form-item> <el-button size="small" type="primary" plain :loading="loading2" @click="calAccuracyRate">计算精准度 @@ -179,7 +170,7 @@ </el-form> <el-form :inline="true" :model="formData" label-width="100px"> <el-row> - <el-col :span="16"> + <el-col :span="18"> <el-form-item label="数据类型"> <el-checkbox-group v-model="formData.chartCheck" @change="changeChartCheck"> <el-checkbox v-for="item in formData.chartOptions" :label="item" @@ -200,7 +191,77 @@ </el-col> </el-row> </el-form> - <div ref="dataAnalysisChart" style="height: 500px;"></div> + <div style="width: 100%;height: 700px;display: flex;flex-direction: row;"> + <div style="height: 100%;width: 80%"> + <div ref="dataAnalysisChart" style="height: 50%;width: 100%"></div> + <div ref="influenceFactorChart" style="height: 50%;width: 100%"></div> + </div> + <div style="width: 20%;height: 100%;"> + <div style="display: flex;flex-direction: row;align-items: center;margin-bottom: 4px;"> + <div style="font-weight: bold;font-size: 14px">影响因素:</div> + <div style="width: calc(100% - 80px);"> + <el-select v-model="influenceFactor" placeholder="请选择" size="small" @change="changeInfluenceFactor"> + <el-option + v-for="(influenceFactor,index) in influenceFactorList" + :key="index" + :label="influenceFactor.factorOutputName" + :value="influenceFactor.factorOutputId" + /> + </el-select> + </div> + </div> + <div class="chart-foot-table p-2" style="width: 100%;height: calc(100% - 20px);overflow-x: hidden;overflow-y: auto;"> + <div style="display: flex;flex-direction: column;align-items: center;margin-bottom: 4px;"> + <span>影响时间</span> + <span style="font-size: 16px;font-weight: bold">{{influenceFactorResultTime}}</span> + </div> + <div v-for="(result, index) in influenceFactorResult" :key="index" style="display: flex;flex-direction: row"> + <span>{{result.factorOutputName}}:</span> + <span>{{result.value}}</span> + </div> + </div> + </div> + </div> + <div class="chart-foot"> + <div class="chart-foot-content"> + <h3 class="chart-foot-title">预警信息</h3> + <div class="chart-foot-table"> + <el-table :data="alarmList" style="width: 100%" v-loading="loadingAlarm" height="100px"> + <el-table-column prop="content" header-align="center" align="left" label="消息内容" min-width="240" /> + <el-table-column prop="alarmType" label="预警类型" header-align="center" align="left" min-width="150"/> + <el-table-column prop="alarmTime" label="预警时间" header-align="center" align="left" min-width="150"/> + </el-table> + </div> + </div> + <div class="chart-foot-content"> + <h3 class="chart-foot-title">调度建议</h3> + <div class="chart-foot-table"> + <el-table :data="suggestList" style="width: 100%" v-loading="loadingAdjust" height="100px"> + <el-table-column + prop="scheduleTime" + label="调度时间" + header-align="center" + align="left" + min-width="160" + /> + <el-table-column + prop="content" + label="内容" + min-width="300" + header-align="center" align="left" + /> + <el-table-column + prop="adjustValue" + label="调整值" + header-align="center" + align="center" + min-width="100" + /> + </el-table> + </div> + + </div> + </div> </div> </div> </div> @@ -209,16 +270,20 @@ </el-card> </template> <script lang="ts" setup> -import {getYMDHMS} from "@/utils/dateUtil" +import {getYMDHMS,formatToDateTime} from "@/utils/dateUtil" import * as McsApi from '@/api/model/mcs' +import * as influenceFactorApi from '@/api/model/pre/influenceFactor/influenceFactorApi' +import * as AlarmMessageApi from '@/api/model/pre/alarm/message' +import * as ScheSuggestApi from '@/api/model/sche/suggest' import * as echarts from "echarts"; import {Search, DArrowLeft, DArrowRight, VideoPlay, VideoPause, CaretLeft, CaretRight} from '@element-plus/icons-vue' +import {lighten} from "@/utils/color"; defineOptions({name: 'AnalysisformData'}) const message = useMessage() // 消息弹窗 const {t} = useI18n() // 国际化 -const dataAnalysisChart = ref(null); + const loading1 = ref(false) // 列表的加载中 const loading2 = ref(false) // 列表的加载中 const total = ref(0) // 列表的总页数 @@ -238,8 +303,8 @@ currentStamp60: '', predictStamp: '', chartCheck: ['T+L', '真实值'], - chartOptions: ['T+N', 'T+L', '当时', '真实值', '调整值', '预测累计', '真实累计'], - checkedItemData: [], + chartOptions: ['T+N', 'T+L','T+L(未调整)', '当时', '真实值', '调整值', '预测累计', '真实累计'], + checkedItemData: undefined, backItem: '', backValue: 0, backCoe: 1, @@ -276,11 +341,20 @@ const treeData = ref([]) const itemDataObject = ref() const timer = ref() -let myChart = null; const isPlay = ref(false) +const alarmList = ref([]) +const suggestList = ref([]) +const loadingAlarm = ref(false) +const loadingAdjust = ref(false) + +// 影响因素结果列表 +const influenceFactorResultList = ref([]) +// 影响因素列表 +const influenceFactorList = ref([]) +// 选中影响因素 +const influenceFactor = ref() const formRules = reactive({ - calItem: [{required: true, message: '预测项不能为空', trigger: 'blur'}], IN_DEVIATION: [{required: true, message: '精准度偏差不能为空', trigger: 'blur'}], OUT_DEVIATION: [{required: true, message: '不可信率偏差不能为空', trigger: 'blur'}], }) @@ -296,6 +370,8 @@ return data.label.includes(value) } +let xAxisData = [] + /** 查询列表 */ const getList = async (isClear = true) => { loading1.value = true @@ -304,25 +380,59 @@ formData.value.chartCheck = ['真实值'] } let chartCheckArray = formData.value.chartCheck; - if (!formData.value.checkedItemData || formData.value.checkedItemData.length == 0) { + if (!formData.value.checkedItemData) { itemData.value.option = {}; return; } - let outIds = formData.value.checkedItemData.map(item => { - return item.id - }) + let outIds = [formData.value.checkedItemData.id] const params = reactive({ outIds: outIds, predictTime: formData.value.predictTime, startTime: formData.value.startTime, endTime: formData.value.endTime }) + + const data = await McsApi.getPreDataCharts(params) formData.value.predictTime = data.predictTime; formData.value.startTime = data.startTime formData.value.endTime = data.endTime - let xAxisData = data.categories; + // 默认影响时间 + changeInfluenceFactorTime(data.predictTime); + + // 获取影响因素结果列表 + influenceFactorResultList.value = await influenceFactorApi.getResultList({ + outIds: outIds, + startTime: data.startTime, + endTime: data.endTime + }) + + // 获取影响因素结果列表 + influenceFactorList.value = await influenceFactorApi.getListByOutId(formData.value.checkedItemData.id) + if (influenceFactorList.value && influenceFactorList.value.length > 0) { + // 根据factorOutputId去重,因为不同的统计规则会有重复的影响因素 + influenceFactorList.value = Array.from(new Map(influenceFactorList.value.map(item => [item.factorOutputId, item])).values()); + // 默认选中第一个影响因素 + influenceFactor.value = influenceFactorList.value?.[0]?.factorOutputId + getInfluenceFactorChart(influenceFactorList.value?.[0]?.factorOutputId) + } + + + const paramsAlarm = reactive({ + outIds: outIds, + predictTime: formData.value.predictTime + }) + + loadingAlarm.value = true + alarmList.value = await AlarmMessageApi.getListByOut(paramsAlarm) + loadingAlarm.value = false + + loadingAdjust.value = true + suggestList.value = await ScheSuggestApi.getListByOut(paramsAlarm) + loadingAdjust.value = false + + xAxisData = data.categories; let defaultYAxis = [ { type: 'value', @@ -476,6 +586,23 @@ } }); } + if (chartCheckArray.indexOf('T+L(未调整)') !== -1) { + let legendName = dataView.resultName + '(T+L(未调整))'; + legendData.push(legendName); + seriesData.push({ + name: legendName, + data: dataView.preDataLOriginal + || [], + type: 'line', + showSymbol: false, + connectNulls: true, + yAxisIndex: yAxisIndex, + smooth: false, + lineStyle: { + width: 2 + } + }); + } if (chartCheckArray.indexOf('当时') !== -1) { let legendName = dataView.resultName + '(当时)'; legendData.push(legendName); @@ -564,7 +691,6 @@ } } - myChart = echarts.init(dataAnalysisChart.value); let option = { title: { text: '' @@ -585,8 +711,8 @@ }, grid: { top: '20%', - left: '3%', - right: '6%', + left: '5%', + right: '5%', bottom: '3%', containLabel: true }, @@ -612,7 +738,12 @@ if (isClear) { myChart.clear() } + myChart.setOption(option) + + + + } finally { loading1.value = false } @@ -620,10 +751,304 @@ calItemBaseVale() } +// 查询影响因素chart +const getInfluenceFactorChart = async (outId) => { + loading1.value = true + try { + + let outIds = [outId] + const params = reactive({ + outIds: outIds, + predictTime: formData.value.predictTime, + startTime: formData.value.startTime, + endTime: formData.value.endTime + }) + + const data = await McsApi.getPreDataCharts(params) + + if (!data?.dataViewList || data.dataViewList.length === 0) { + myInfluenceFactorChart.clear() + return + } + + xAxisData = data.categories; + let defaultYAxis = [ + { + type: 'value', + name: "累计值", + splitLine: {show: false}, + axisLine: {show: true}, + position: 'right' + }, + { + type: 'value', + name: "", + splitLine: {show: false}, + axisLine: {show: true}, + position: 'left' + } + ]; + let yAxisData = []; + let offset = 0; + let yAxisIndex = 0; + let legendData = []; + let yMaxArr = []; + let seriesData = []; + seriesData.push({ + name: '', + data: [null], + type: 'line', + smooth: true, + color: 'green', + markLine: { + silent: true, + lineStyle: { + color: '#32a487', + width: 2 + }, + data: [{ + xAxis: formData.value.predictTime + }], + label: { + normal: { + formatter: formData.value.predictTime + } + }, + symbol: ['circle', 'none'], + }, + }); + itemDataObject.value = {} + yAxisData.push({ + type: 'value', + name: "累计值", + position: 'right', + splitLine: { + show: false + }, + axisLine: { + show: true, + lineStyle: {} + }, + axisLabel: { + formatter: '{value}' + } + }) + for (let i = 0; i < data.dataViewList.length; i++) { + let dataView = data.dataViewList[i] + itemDataObject.value[dataView.outId] = dataView; + let maxValue = dataView.maxValue; + let minValue = dataView.minValue; + yAxisIndex = (formData.value.isMultipleY ? i : 0) + 1; + let yMax = maxValue; + if (maxValue < 0) { + maxValue = 1; + } else if (maxValue < 10) { + yMax = (Math.ceil(maxValue * 11) / 10).toFixed(1); + } else if (maxValue < 100) { + yMax = (Math.ceil(maxValue * 1.1 / 5) * 5); + } else { + yMax = (Math.ceil(maxValue * 1.1 / 10) * 10); + } + yMaxArr.push(yMax); + let yMin = minValue; + if (minValue >= 0) { + yMin = 0; + } else if (minValue > -10) { + yMin = (Math.floor(minValue * 11) / 10).toFixed(1); + } else if (minValue > -100) { + yMin = (Math.floor(minValue * 1.1 / 5) * 5); + } else { + yMin = (Math.floor(minValue * 1.1 / 10) * 10); + } + yAxisData.push({ + type: 'value', + name: "", + min: yMin, + max: yMax, + position: 'left', + offset: offset, + splitLine: { + show: false + }, + axisLine: { + show: true, + lineStyle: {} + }, + axisLabel: { + formatter: '{value}' + } + }) + offset = offset + 40 + //真实值 + legendData.push(dataView.resultName + '(真实)'); + seriesData.push({ + name: dataView.resultName + '(真实)', + data: dataView.realData || [], + type: 'line', + yAxisIndex: yAxisIndex, + showSymbol: false, + smooth: false, + lineStyle: { + width: 2 + } + }); + //T+L + legendData.push(dataView.resultName + '(T+L)'); + seriesData.push({ + name: dataView.resultName + '(T+L)', + data: dataView.preDataL || [], + type: 'line', + showSymbol: false, + connectNulls: true, + yAxisIndex: yAxisIndex, + smooth: false, + lineStyle: { + width: 2 + } + }); + // 当时 + legendData.push(dataView.resultName + '(当时)'); + seriesData.push({ + name: dataView.resultName + '(当时)', + data: dataView.curData || [], + type: 'line', + yAxisIndex: yAxisIndex, + showSymbol: true, + smooth: false, + lineStyle: { + width: 3 + } + }); + //预测累计 + legendData.push(dataView.resultName + '(预测累计)'); + seriesData.push({ + name: dataView.resultName + '(预测累计)', + data: dataView.cumulantPreData || [], + type: 'line', + yAxisIndex: 0, + showSymbol: false, + connectNulls: true, + smooth: false, + lineStyle: { + width: 2, + type: 'dashed' + } + }); + // 真实累计 + legendData.push(dataView.resultName + '(真实累计)'); + seriesData.push({ + name: dataView.resultName + '(真实累计)', + data: dataView.cumulantRealData || [], + type: 'line', + yAxisIndex: 0, + showSymbol: false, + connectNulls: true, + smooth: false, + lineStyle: { + width: 2, + type: 'dashed' + } + }); + } + //如果最大值相差不大,改成一致大小 + if (yMaxArr.length > 1) { + let max = Math.max.apply(null, yMaxArr); + let min = Math.min.apply(null, yMaxArr); + if (Math.abs((max - min) / max) <= 0.2) { + for (let i = 0; i < yAxisData.length; i++) { + yAxisData[i].max = max; + } + } + } + + let option = { + title: { + text: '' + }, + tooltip: { + trigger: 'axis' + }, + toolbox: { + show: true, + feature: { + saveAsImage: {} + } + }, + legend: { + show: true, + data: legendData, + top: 10 + }, + grid: { + top: '20%', + left: '5%', + right: '5%', + bottom: '3%', + containLabel: true + }, + xAxis: { + type: 'category', + boundaryGap: false, + data: xAxisData + }, + yAxis: formData.value.isMultipleY ? yAxisData : defaultYAxis, + dataZoom: [ + { + type: 'inside', + start: 0, + end: 100 + }, + { + start: 0, + end: 10 + } + ], + series: seriesData + } + myInfluenceFactorChart.clear() + myInfluenceFactorChart.setOption(option) + } finally { + loading1.value = false + } +} + + + onMounted(() => { + initChart() resetForm() getPreItemTree() }) + +const dataAnalysisChart = ref(); +const influenceFactorChart = ref(); +let myChart = ref({}); +let myInfluenceFactorChart = ref({}); + +function initChart() { + myChart = echarts.init(dataAnalysisChart.value) + myInfluenceFactorChart = echarts.init(influenceFactorChart.value) + // 监听点击事件 + myChart.getZr().on('click', 'series.line',function (params) { + var pointInPixel = [params.offsetX, params.offsetY]; + var pointInData = myChart.convertFromPixel('grid', pointInPixel); + const time = xAxisData[pointInData[0]]; + changeInfluenceFactorTime(time) + }); +} + +let influenceFactorResult = ref([]) +let influenceFactorResultTime = ref('') +// 影响因素时间改变 +function changeInfluenceFactorTime(time) { + if (time && new Date(time)?.getTime()) { + influenceFactorResultTime.value = time + influenceFactorResult.value = influenceFactorResultList.value?.[formData.value.checkedItemData?.id]?.filter(e => e.time === new Date(time).getTime()).sort((a, b) => b.value - a.value) || []; + } +}// 选择影响因素 +function changeInfluenceFactor(value) { + getInfluenceFactorChart(value) +} async function getPreItemTree() { treeData.value = await McsApi.getPredictItemTree() @@ -704,16 +1129,21 @@ } function onCheckTree(data, checked, indeterminate) { - formData.value.checkedItemData = []; - if (checked.checkedNodes) { - let cns = [...checked.checkedNodes] - for (let i = 0; i < cns.length; i++) { - if (cns[i].id.indexOf('-') !== -1) { - continue - } - formData.value.checkedItemData.push(cns[i]) - } - } + // 单选 + treeRef.value.setCheckedKeys([]) + treeRef.value.setCheckedNodes([data]) + + formData.value.checkedItemData = data + calRateForm.value.calItem = data.id + // if (checked.checkedNodes) { + // let cns = [...checked.checkedNodes] + // for (let i = 0; i < cns.length; i++) { + // if (cns[i].id.indexOf('-') !== -1) { + // continue + // } + // formData.value.checkedItemData.push(cns[i]) + // } + // } debounce(getList, 1000); } @@ -750,8 +1180,8 @@ calRateForm.value.realCumulant = dataView.hisCumulant; calDeviation(dataView.realData,dataView.preDataL,'deviation') calDeviation(dataView.cumulantRealData,dataView.cumulantPreData,'deviationCumulant') + calAccuracyRate() } - calAccuracyRate() } function calAccuracyRate() { @@ -861,8 +1291,8 @@ currentStamp60: '', predictStamp: '', chartCheck: ['T+L', '真实值'], - chartOptions: ['T+N', 'T+L', '当时', '真实值', '调整值', '预测累计', '真实累计'], - checkedItemData: [], + chartOptions: ['T+N', 'T+L','T+L(未调整)', '当时', '真实值', '调整值', '预测累计', '真实累计'], + checkedItemData: undefined, backItem: '', backValue: 0, backCoe: 1, @@ -894,6 +1324,23 @@ } </script> <style scoped> +.chart-foot-table { + border: 1px solid #bababa; +} +.chart-foot-title { + font-size: 14px; +} +.chart-foot-content { + height: 100%; + width: 50%; + padding: 5px; +} +.chart-foot { + height: 120px; + width: 100%; + display: flex; + flex-direction: row; +} .el-form-item { margin-bottom: 0 !important; } @@ -902,6 +1349,8 @@ height: 100%; border: 1px solid lightgray; padding: 10px; + overflow-y: auto; + overflow-x: hidden; } .his-body-tree { @@ -925,7 +1374,7 @@ .his-body { width: 100%; - height: calc(calc(100vh - 68px - 38px - 160px)); + height: calc(calc(100vh - 68px - 38px - 60px)); display: flex; flex-direction: row; justify-content: flex-start; -- Gitblit v1.9.3