Jay
2024-09-30 d7fad0f8d38ee60bd39634f814857c70a4b51d26
预测数据分析前端代码
已修改2个文件
已添加1个文件
712 ■■■■■ 文件已修改
src/api/model/pre/predict/index.ts 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/dateUtil.ts 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/model/pre/analysis/index.vue 687 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
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})
}
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
}
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>