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