From d7fad0f8d38ee60bd39634f814857c70a4b51d26 Mon Sep 17 00:00:00 2001
From: Jay <csj123456>
Date: 星期一, 30 九月 2024 17:15:35 +0800
Subject: [PATCH] 预测数据分析前端代码

---
 src/utils/dateUtil.ts                  |   17 +
 src/api/model/pre/predict/index.ts     |    8 
 src/views/model/pre/analysis/index.vue |  687 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 712 insertions(+), 0 deletions(-)

diff --git a/src/api/model/pre/predict/index.ts b/src/api/model/pre/predict/index.ts
index 677b35a..e27d8c4 100644
--- a/src/api/model/pre/predict/index.ts
+++ b/src/api/model/pre/predict/index.ts
@@ -130,3 +130,11 @@
     httpRequest
   }
 }
+
+export const getMmPredictItemTree = () => {
+  return request.get({ url: `/model/pre/item/tree`})
+}
+
+export const getViewCharts = (params) => {
+  return request.get({ url: `/model/pre/item/view-charts`,params})
+}
diff --git a/src/utils/dateUtil.ts b/src/utils/dateUtil.ts
index b83c3a5..658f0a1 100644
--- a/src/utils/dateUtil.ts
+++ b/src/utils/dateUtil.ts
@@ -21,3 +21,20 @@
 }
 
 export const dateUtil = dayjs
+
+export function getYMDHMS (timestamp: number | string | Date) {
+  const time = new Date(timestamp)
+  const year = time.getFullYear()
+  let month = time.getMonth() + 1 + ''
+  let date = time.getDate() + ''
+  let hours = time.getHours() + ''
+  let minute = time.getMinutes() + ''
+  let second = time.getSeconds() + ''
+
+  if (month < 10) { month = '0' + month }
+  if (date < 10) { date = '0' + date }
+  if (hours < 10) { hours = '0' + hours }
+  if (minute < 10) { minute = '0' + minute }
+  if (second < 10) { second = '0' + second }
+  return year + '-' + month + '-' + date + ' ' + hours + ':' + minute + ':' + second
+}
diff --git a/src/views/model/pre/analysis/index.vue b/src/views/model/pre/analysis/index.vue
new file mode 100644
index 0000000..f61540c
--- /dev/null
+++ b/src/views/model/pre/analysis/index.vue
@@ -0,0 +1,687 @@
+<template>
+  <el-card shadow="never" class="aui-card--fill">
+    <div class="mod-his__index">
+      <el-form :inline="true" :model="formData" label-width="80px">
+        <el-form-item label="开始时间">
+          <el-date-picker
+              size="mini"
+              v-model="formData.startTime"
+              type="datetime"
+              placeholder="选择日期时间"/>
+        </el-form-item>
+        <el-form-item label="结束时间">
+          <el-date-picker
+              size="mini"
+              v-model="formData.endTime"
+              type="datetime"
+              placeholder="选择日期时间"/>
+        </el-form-item>
+        <el-form-item label="预测时间">
+          <el-date-picker
+              size="mini"
+              v-model="formData.predictTime"
+              type="datetime"
+              placeholder="选择日期时间"/>
+        </el-form-item>
+        <el-form-item label="预测频率">
+          <el-input-number size="mini" v-model="formData.predictFreq" controls-position="right" :min="1"
+                           :max="10"/>
+        </el-form-item>
+        <el-form-item>
+          <el-button-group>
+            <el-button size="mini" type="primary" plain :icon="ArrowLeft"
+                       v-loading="loading1" @click="leftSearchDataByRange()"/>
+            <el-button size="mini" type="primary" plain :icon="Search"
+                       v-loading="loading1" @click="getList()">查询</el-button>
+            <el-button size="mini" type="primary" plain :icon="ArrowRight"
+                       v-loading="loading1" @click="rightSearchDataByRange()"/>
+          </el-button-group>
+        </el-form-item>
+
+        <div class="his-body">
+          <div class="his-body-left">
+            <div class="his-body-tree">
+              <el-tree
+                  :data="treeData"
+                  show-checkbox
+                  node-key="id"
+                  ref="tree"
+                  highlight-current
+                  :props="defaultProps"
+                  @check="onCheckTree"/>
+            </div>
+          </div>
+          <div class="his-body-right">
+            <div class="his-body-chart">
+              <el-form :inline="true" :model="calRateForm" ref="calRateForm" label-width="100px">
+                <el-row>
+                  <el-col :span="6" >
+                    <el-form-item label="预测项" prop="calItem" style="width: 90%">
+                      <el-select v-model="calRateForm.calItem" @change="calItemBaseVale" placeholder="请选择">
+                        <el-option
+                            v-for="item in formData.checkedItemData"
+                            :key="item.id"
+                            :label="item.label"
+                            :value="item.id"/>
+                      </el-select>
+                    </el-form-item>
+                  </el-col>
+                  <el-col :span="6">
+                    <el-form-item label="精准度偏差" prop="IN_DEVIATION">
+                      <el-input-number size="mini" v-model="calRateForm.IN_DEVIATION" controls-position="right" :min="1"
+                                       :max="10"/>
+                    </el-form-item>
+                  </el-col>
+                  <el-col :span="6">
+                    <el-form-item label="不可信率偏差" prop="OUT_DEVIATION">
+                      <el-input-number size="mini" v-model="calRateForm.OUT_DEVIATION" controls-position="right"
+                                       :min="1"
+                                       :max="20"/>
+                    </el-form-item>
+                  </el-col>
+                  <el-col :span="4">
+                    <el-form-item>
+                      <el-button size="mini" type="primary" plain :loading="loading2" @click="calAccuracyRate">计算精准度
+                      </el-button>
+                    </el-form-item>
+                  </el-col>
+                </el-row>
+                <el-row>
+                  <el-col :span="4">
+                    <el-form-item label="精准度:">
+                      {{calRateForm.IN_ACCURACY_RATE}}%
+                    </el-form-item>
+                  </el-col>
+                  <el-col :span="4">
+                    <el-form-item label="预测平均值:">
+                      {{calRateForm.itemPreAvg}}
+                    </el-form-item>
+                  </el-col>
+                  <el-col :span="4">
+                    <el-form-item label="预测最大值:">
+                      {{calRateForm.itemPreMax}}
+                    </el-form-item>
+                  </el-col>
+                  <el-col :span="4">
+                    <el-form-item label="预测最小值:">
+                      {{calRateForm.itemPreMin}}
+                    </el-form-item>
+                  </el-col>
+                  <el-col :span="4">
+                    <el-form-item label="预测累积量:">
+                      {{calRateForm.preCumulant}}
+                    </el-form-item>
+                  </el-col>
+                </el-row>
+                <el-row>
+                  <el-col :span="4">
+                    <el-form-item label="不可信率:">
+                      {{calRateForm.OUT_ACCURACY_RATE}}%
+                    </el-form-item>
+                  </el-col>
+                  <el-col :span="4">
+                    <el-form-item label="真实平均值:">
+                      {{calRateForm.itemAvg}}
+                    </el-form-item>
+                  </el-col>
+                  <el-col :span="4">
+                    <el-form-item label="真实最大值:">
+                      {{calRateForm.itemMax}}
+                    </el-form-item>
+                  </el-col>
+                  <el-col :span="4">
+                    <el-form-item label="真实最小值:">
+                      {{calRateForm.itemMin}}
+                    </el-form-item>
+                  </el-col>
+                  <el-col :span="4">
+                    <el-form-item label="真实累积量:">
+                      {{calRateForm.realCumulant}}
+                    </el-form-item>
+                  </el-col>
+                </el-row>
+              </el-form>
+              <el-form :inline="true" :model="formData" label-width="100px">
+                <el-row>
+                  <el-col :span="12">
+                    <el-form-item label="数据类型">
+                      <el-checkbox-group v-model="formData.chartCheck" @change="changeChartCheck">
+                        <el-checkbox v-for="item in formData.chartOptions" :label="item" :key="item">{{item}}
+                        </el-checkbox>
+                      </el-checkbox-group>
+                    </el-form-item>
+                  </el-col>
+                  <el-col :span="6">
+                    <el-form-item>
+                      <el-radio v-model="formData.isMultipleY" :label="false" @input="onChangeMultipleY">单坐标轴</el-radio>
+                      <el-radio v-model="formData.isMultipleY" :label="true" @input="onChangeMultipleY">多坐标轴</el-radio>
+                    </el-form-item>
+                  </el-col>
+                </el-row>
+              </el-form>
+              <div id="data-analysis" style="height: 500px;"></div>
+            </div>
+          </div>
+        </div>
+      </el-form>
+
+    </div>
+  </el-card>
+</template>
+<script lang="ts" setup>
+  import {getYMDHMS} from "@/utils/dateUtil"
+  import * as CategoryApi from "@/api/data/ind/category";
+  import * as DmModule from '@/api/model/pre/dm'
+  import * as ItemApi from "@/api/data/ind/item/item";
+  import * as MmPredictItem from '@/api/model/pre/predict'
+  import * as echarts from "echarts";
+  import { onMounted, ref } from 'vue';
+  import { Search, ArrowLeft, ArrowRight,} from '@element-plus/icons-vue'
+
+  defineOptions({name: 'AnalysisformData'})
+
+  const message = useMessage() // 消息弹窗
+  const { t } = useI18n() // 国际化
+  const dataCategoryList = ref([] as CategoryApi.IndItemCategoryVO[])
+  const loading1 = ref(false) // 列表的加载中
+  const loading2 = ref(false) // 列表的加载中
+  const total = ref(0) // 列表的总页数
+  const list = ref([]) // 字典表格数据
+  const queryParams = reactive({
+    pageNo: 1,
+    pageSize: 10,
+    itemno: '',
+    itemname: '',
+  })
+  let formData = ref({
+    rangeDate: '',
+    startTime: '',
+    endTime: '',
+    predictTime: '',
+    predictTimeStr: '',
+    startTimeStr: '',
+    endTimeStr: '',
+    predictTimeStamp: 0,
+    startTimeStamp: 0,
+    endTimeStamp: 0,
+    currentStamp: '',
+    currentStamp60: '',
+    predictStamp: '',
+    chartCheck: ['T+L', '真实值'],
+    chartOptions: ['T+N', 'T+L', '当时', '真实值', '调整值'],
+    checkedItemData: [],
+    backItem: '',
+    backValue: 0,
+    backCoe: 1,
+    preCumulant: 0,
+    realCumulant: 0,
+    queryStep: 2,
+    isMultipleYRadio: '单坐标轴',
+    isMultipleY: false,
+    predictFreq: 3,
+  })
+  let calRateForm = ref({
+    calItem: '',
+    IN_DEVIATION: 0,
+    OUT_DEVIATION: 0,
+    IN_ACCURACY_RATE: 0,
+    OUT_ACCURACY_RATE: 0,
+    itemAvg: 0,
+    itemMax: 0,
+    itemMin: 0,
+    itemPreAvg: 0,
+    itemPreMax: 0,
+    itemPreMin: 0,
+    preCumulant:0,
+    realCumulant:0
+  })
+  let itemData = ref({
+    currentTreeList: [],
+    chart: {},
+    option: {}
+  })
+  const chartContainer = ref(null);
+  const treeData = ref([])
+  const itemDataObject = ref()
+  const timer = ref()
+  //const myChart = echarts.init(document.getElementById("data-analysis"));
+  /** 查询列表 */
+  const getList = async () => {
+    loading1.value = true
+    try {
+      const data = formData.value
+      if (!formData.value.chartCheck) {
+        formData.value.chartCheck = ['真实值']
+      }
+      let chartCheckArray = formData.value.chartCheck;
+      if (!formData.value.checkedItemData || formData.value.checkedItemData.length == 0) {
+        itemData.value.option = {};
+        return;
+      }
+      let itemIdList = formData.value.checkedItemData.map(item => {
+        return item.id
+      })
+      const params = ref({
+        itemIds: itemIdList.join(','),
+        predictTime: formData.value.predictTime,
+        startTime: formData.value.startTime,
+        endTime: formData.value.endTime
+      })
+      const res = await MmPredictItem.getViewCharts(params)
+      if (res.code !== 0) {
+        return message.error(res.msg)
+      }
+      formData.value.predictTime = res.data.predictTime;
+      formData.value.startTime = res.data.startTime
+      formData.value.endTime = res.data.endTime
+
+      let xAxisData = res.data.categories;
+      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 = {}
+      for (let i = 0; i < res.data.dataViewList.length; i++) {
+        let dataView = res.data.dataViewList[i]
+        itemDataObject.value.dataView.itemId = dataView;
+        let maxValue = dataView.maxValue;
+        let minValue = dataView.minValue;
+        yAxisIndex = formData.value.isMultipleY ? i : 0;
+        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
+        if (chartCheckArray.indexOf('真实值') !== -1) {
+          let legendName = dataView.itemName + '(真实)';
+          legendData.push(legendName);
+          seriesData.push({
+            name: legendName,
+            data: dataView.realData || [],
+            type: 'line',
+            yAxisIndex: yAxisIndex,
+            showSymbol: false,
+            smooth: true,
+            lineStyle: {
+              width: 3
+            }
+          });
+        }
+        if (chartCheckArray.indexOf('T+N') !== -1) {
+          let legendName = dataView.itemName + '(T+N)';
+          seriesData.push({
+            name: legendName,
+            data: dataView.preDataN || [],
+            type: 'line',
+            yAxisIndex: yAxisIndex,
+            showSymbol: false,
+            smooth: true,
+            lineStyle: {
+              width: 3
+            }
+          });
+        }
+        if (chartCheckArray.indexOf('T+L') !== -1) {
+          let legendName = dataView.itemName + '(T+L)';
+          legendData.push(legendName);
+          seriesData.push({
+            name: legendName,
+            data: dataView.preDataL || [],
+            type: 'line',
+            showSymbol: false,
+            connectNulls: true,
+            yAxisIndex: yAxisIndex,
+            smooth: true,
+            lineStyle: {
+              width: 3
+            }
+          });
+        }
+        if (chartCheckArray.indexOf('当时') !== -1) {
+          let legendName = dataView.itemName + '(当时)';
+          legendData.push(legendName);
+          seriesData.push({
+            name: legendName,
+            data: dataView.curData || [],
+            type: 'line',
+            yAxisIndex: yAxisIndex,
+            showSymbol: false,
+            smooth: true,
+            lineStyle: {
+              width: 3
+            }
+          });
+        }
+        if (chartCheckArray.indexOf('调整值') !== -1) {
+          let legendName = dataView.itemName + '(调整值)';
+          legendData.push(legendName);
+          seriesData.push({
+            name: legendName,
+            data: dataView.adjData || [],
+            type: 'line',
+            yAxisIndex: yAxisIndex,
+            showSymbol: false,
+            connectNulls: true,
+            smooth: true,
+            lineStyle: {
+              width: 3,
+              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'
+        },
+        legend: {
+          show: true,
+          data: legendData,
+          top: 10
+        },
+        grid: {
+          top: 50,
+          left: '3%',
+          right: '6%',
+          bottom: '3%',
+          containLabel: true
+        },
+        xAxis: {
+          type: 'category',
+          boundaryGap: false,
+          data: xAxisData
+        },
+        yAxis: formData.value.isMultipleY ? yAxisData : {
+          type: 'value',
+          splitLine: {show: false},
+          axisLine: {show: true}
+        },
+        dataZoom: [
+          {
+            type: 'inside',
+            start: 0,
+            end: 100
+          },
+          {
+            start: 0,
+            end: 10
+          }
+        ],
+        series: seriesData
+      }
+      //chart.setOption(option)
+    } finally {
+      loading1.value = false
+    }
+  }
+
+  onMounted(() => {
+    getPreItemTree()
+    getList()
+  })
+
+  async function getPreItemTree() {
+    treeData.value = await MmPredictItem.getMmPredictItemTree()
+  }
+
+  function leftSearchDataByRange() {
+    let mins = getRangeMins();
+    let startTime = formData.value.startTime;
+    let endTime = formData.value.endTime;
+    let predictTime = formData.value.predictTime;
+    if (predictTime) {
+      predictTime = getYMDHMS(new Date(predictTime) - 1000 * 60 * mins);
+      formData.value.predictTime = predictTime;
+    }
+    if (startTime) {
+      startTime = getYMDHMS(new Date(startTime) - 1000 * 60 * mins);
+      formData.value.startTime = startTime;
+    }
+    if (endTime) {
+      endTime = getYMDHMS(new Date(endTime) - 1000 * 60 * mins);
+      formData.value.endTime = endTime;
+    }
+    getList();
+  }
+
+  function getRangeMins () {
+    let result: string | number = 0;
+    if(formData.value.startTime && formData.value.endTime) {
+      let startStamp = new Date(formData.value.startTime).getTime();
+      let endStamp = new Date(formData.value.endTime).getTime();
+      let queryStep = ((endStamp - startStamp) / (1000 * 60)).toFixed(0);
+      result = queryStep >= 0 ? queryStep : 0;
+    }
+    return result;
+  }
+
+  function onCheckTree(data, checked, indeterminate) {
+    formData.value.checkedItemData = [];
+    if (checked.checkedNodes) {
+      formData.value.checkedItemData = [...checked.checkedNodes]
+    }
+    //myChart.clear()
+    debounce(getList(), 1000);
+  }
+
+  function debounce(func, wait) {
+    let args = [];
+    if (timer.value) {
+      clearTimeout(timer.value);
+    }
+    timer.value = setTimeout(() => {
+      func.apply(this, args);
+      timer.value = null;
+    }, wait)
+  }
+
+  function calItemBaseVale() {
+    if (!calRateForm.value.calItem) {
+      calRateForm.value.itemPreMax = 0;
+      calRateForm.value.itemPreMin = 0;
+      calRateForm.value.itemPreAvg = 0;
+      calRateForm.value.preCumulant = 0;
+      calRateForm.value.itemMax = 0;
+      calRateForm.value.itemMin = 0;
+      calRateForm.value.itemAvg = 0;
+      calRateForm.value.realCumulant = 0;
+      return
+    } else {
+      let dataView = itemDataObject[calRateForm.value.calItem]
+      calRateForm.value.itemPreMax = dataView.preMax;
+      calRateForm.value.itemPreMin = dataView.preMin;
+      calRateForm.value.itemPreAvg = dataView.preAvg;
+      calRateForm.value.preCumulant = dataView.preCumulant;
+      calRateForm.value.itemMax = dataView.hisMax;
+      calRateForm.value.itemMin = dataView.hisMin;
+      calRateForm.value.itemAvg = dataView.hisAvg;
+      calRateForm.value.realCumulant = dataView.hisCumulant;
+    }
+  }
+
+  function calAccuracyRate() {
+    this.$refs['calRateForm'].validate((valid) => {
+      if (!valid) {
+        return false
+      }
+      let dataView = itemDataObject[calRateForm.value.calItem]
+      let seriesReaData = dataView.realData;
+      let seriesPreData = dataView.preDataL;
+      if (seriesReaData == null || seriesPreData == null ||
+        seriesReaData.length === 0 || seriesPreData.length === 0) {
+        loading2.value = false;
+        return;
+      }
+      let predictValueMap = {};
+      seriesPreData.forEach(function (item) {
+        predictValueMap[item[0]] = item[1];
+      })
+      let pointValueMap = {};
+      seriesReaData.forEach(function (item) {
+        pointValueMap[item[0]] = item[1];
+      })
+      let inDeviation = Number(calRateForm.value.IN_DEVIATION);
+      let outDeviation = Number(calRateForm.value.OUT_DEVIATION);
+      if (inDeviation === 0 && outDeviation === 0) {
+        loading2.value = false;
+        return;
+      }
+      let inDeviationCount = 0;
+      let outDeviationCount = 0;
+      let totalCount = 0;
+      for (let key in predictValueMap) {
+        let predictValue = predictValueMap[key];
+        let pointValue = pointValueMap[key];
+        if (pointValue == null || "" === pointValue || predictValue == null || "" === predictValue) {
+          continue;
+        }
+        let deviationAbs = (predictValue - pointValue) >= 0 ? (predictValue - pointValue) : (predictValue - pointValue) * -1;
+        if (deviationAbs < inDeviation) {
+          inDeviationCount = inDeviationCount + 1;
+        }
+        if (deviationAbs > outDeviation) {
+          outDeviationCount = outDeviationCount + 1;
+        }
+        totalCount = totalCount + 1;
+      }
+
+      let rateIn = (inDeviationCount / totalCount * 100).toFixed(2);
+      let rateOut = (outDeviationCount / totalCount * 100).toFixed(2);
+      calRateForm.value.IN_ACCURACY_RATE = Number(rateIn);
+      calRateForm.value.OUT_ACCURACY_RATE = Number(rateOut);
+      loading2.value = false;
+    })
+  }
+
+  function rightSearchDataByRange() {
+    let mins = getRangeMins();
+    let startTime = formData.value.startTime;
+    let endTime = formData.value.endTime;
+    let predictTime = formData.value.predictTime;
+    if (predictTime) {
+      predictTime = getYMDHMS(new Date(predictTime) - 0 + 1000 * 60 * mins);
+      formData.value.predictTime = predictTime;
+    }
+    if (startTime) {
+      startTime = getYMDHMS(new Date(startTime) - 0 + 1000 * 60 * mins);
+      formData.value.startTime = startTime;
+    }
+    if (endTime) {
+      endTime = getYMDHMS(new Date(endTime) - 0 + 1000 * 60 * mins);
+      formData.value.endTime = endTime;
+    }
+    getList();
+  }
+
+</script>
+<style scoped>
+  .el-form-item {
+    margin-bottom: 0 !important;
+  }
+
+  .his-body-chart {
+    height: 100%;
+    border: 1px solid lightgray;
+    padding: 10px;
+  }
+
+  .his-body-tree {
+    height: 100%;
+    border: 1px solid lightgray;
+    padding: 10px;
+  }
+
+  .his-body-right {
+    width: 80%;
+    height: 100%;
+    padding-top: 10px;
+  }
+
+  .his-body-left {
+    width: 20%;
+    height: 100%;
+    padding: 10px 10px 0 0;
+  }
+
+  .his-body {
+    width: 100%;
+    height: calc(calc(100vh - 48px - 38px - 130px));
+    display: flex;
+    flex-direction: row;
+    justify-content: flex-start;
+    align-content: flex-start;
+  }
+</style>

--
Gitblit v1.9.3