Jay
2024-09-30 f26b8f425655c86a01e0338b5c8482a919242f2f
提交 | 用户 | 时间
d7fad0 1 <template>
J 2   <el-card shadow="never" class="aui-card--fill">
3     <div class="mod-his__index">
4       <el-form :inline="true" :model="formData" label-width="80px">
5         <el-form-item label="开始时间">
6           <el-date-picker
7               size="mini"
8               v-model="formData.startTime"
9               type="datetime"
10               placeholder="选择日期时间"/>
11         </el-form-item>
12         <el-form-item label="结束时间">
13           <el-date-picker
14               size="mini"
15               v-model="formData.endTime"
16               type="datetime"
17               placeholder="选择日期时间"/>
18         </el-form-item>
19         <el-form-item label="预测时间">
20           <el-date-picker
21               size="mini"
22               v-model="formData.predictTime"
23               type="datetime"
24               placeholder="选择日期时间"/>
25         </el-form-item>
26         <el-form-item label="预测频率">
27           <el-input-number size="mini" v-model="formData.predictFreq" controls-position="right" :min="1"
28                            :max="10"/>
29         </el-form-item>
30         <el-form-item>
31           <el-button-group>
32             <el-button size="mini" type="primary" plain :icon="ArrowLeft"
33                        v-loading="loading1" @click="leftSearchDataByRange()"/>
34             <el-button size="mini" type="primary" plain :icon="Search"
35                        v-loading="loading1" @click="getList()">查询</el-button>
36             <el-button size="mini" type="primary" plain :icon="ArrowRight"
37                        v-loading="loading1" @click="rightSearchDataByRange()"/>
38           </el-button-group>
39         </el-form-item>
40
41         <div class="his-body">
42           <div class="his-body-left">
43             <div class="his-body-tree">
44               <el-tree
45                   :data="treeData"
46                   show-checkbox
47                   node-key="id"
48                   ref="tree"
49                   highlight-current
50                   :props="defaultProps"
51                   @check="onCheckTree"/>
52             </div>
53           </div>
54           <div class="his-body-right">
55             <div class="his-body-chart">
f26b8f 56               <el-form :inline="true" :model="calRateForm" :rules="formRules" ref="calRateForm" label-width="108px">
d7fad0 57                 <el-row>
J 58                   <el-col :span="6" >
59                     <el-form-item label="预测项" prop="calItem" style="width: 90%">
60                       <el-select v-model="calRateForm.calItem" @change="calItemBaseVale" placeholder="请选择">
61                         <el-option
62                             v-for="item in formData.checkedItemData"
63                             :key="item.id"
64                             :label="item.label"
65                             :value="item.id"/>
66                       </el-select>
67                     </el-form-item>
68                   </el-col>
69                   <el-col :span="6">
70                     <el-form-item label="精准度偏差" prop="IN_DEVIATION">
71                       <el-input-number size="mini" v-model="calRateForm.IN_DEVIATION" controls-position="right" :min="1"
72                                        :max="10"/>
73                     </el-form-item>
74                   </el-col>
75                   <el-col :span="6">
76                     <el-form-item label="不可信率偏差" prop="OUT_DEVIATION">
77                       <el-input-number size="mini" v-model="calRateForm.OUT_DEVIATION" controls-position="right"
78                                        :min="1"
79                                        :max="20"/>
80                     </el-form-item>
81                   </el-col>
82                   <el-col :span="4">
83                     <el-form-item>
84                       <el-button size="mini" type="primary" plain :loading="loading2" @click="calAccuracyRate">计算精准度
85                       </el-button>
86                     </el-form-item>
87                   </el-col>
88                 </el-row>
89                 <el-row>
90                   <el-col :span="4">
91                     <el-form-item label="精准度:">
92                       {{calRateForm.IN_ACCURACY_RATE}}%
93                     </el-form-item>
94                   </el-col>
95                   <el-col :span="4">
96                     <el-form-item label="预测平均值:">
97                       {{calRateForm.itemPreAvg}}
98                     </el-form-item>
99                   </el-col>
100                   <el-col :span="4">
101                     <el-form-item label="预测最大值:">
102                       {{calRateForm.itemPreMax}}
103                     </el-form-item>
104                   </el-col>
105                   <el-col :span="4">
106                     <el-form-item label="预测最小值:">
107                       {{calRateForm.itemPreMin}}
108                     </el-form-item>
109                   </el-col>
110                   <el-col :span="4">
111                     <el-form-item label="预测累积量:">
112                       {{calRateForm.preCumulant}}
113                     </el-form-item>
114                   </el-col>
115                 </el-row>
116                 <el-row>
117                   <el-col :span="4">
118                     <el-form-item label="不可信率:">
119                       {{calRateForm.OUT_ACCURACY_RATE}}%
120                     </el-form-item>
121                   </el-col>
122                   <el-col :span="4">
123                     <el-form-item label="真实平均值:">
124                       {{calRateForm.itemAvg}}
125                     </el-form-item>
126                   </el-col>
127                   <el-col :span="4">
128                     <el-form-item label="真实最大值:">
129                       {{calRateForm.itemMax}}
130                     </el-form-item>
131                   </el-col>
132                   <el-col :span="4">
133                     <el-form-item label="真实最小值:">
134                       {{calRateForm.itemMin}}
135                     </el-form-item>
136                   </el-col>
137                   <el-col :span="4">
138                     <el-form-item label="真实累积量:">
139                       {{calRateForm.realCumulant}}
140                     </el-form-item>
141                   </el-col>
142                 </el-row>
143               </el-form>
144               <el-form :inline="true" :model="formData" label-width="100px">
145                 <el-row>
146                   <el-col :span="12">
147                     <el-form-item label="数据类型">
148                       <el-checkbox-group v-model="formData.chartCheck" @change="changeChartCheck">
149                         <el-checkbox v-for="item in formData.chartOptions" :label="item" :key="item">{{item}}
150                         </el-checkbox>
151                       </el-checkbox-group>
152                     </el-form-item>
153                   </el-col>
154                   <el-col :span="6">
155                     <el-form-item>
156                       <el-radio v-model="formData.isMultipleY" :label="false" @input="onChangeMultipleY">单坐标轴</el-radio>
157                       <el-radio v-model="formData.isMultipleY" :label="true" @input="onChangeMultipleY">多坐标轴</el-radio>
158                     </el-form-item>
159                   </el-col>
160                 </el-row>
161               </el-form>
162               <div id="data-analysis" style="height: 500px;"></div>
163             </div>
164           </div>
165         </div>
166       </el-form>
167
168     </div>
169   </el-card>
170 </template>
171 <script lang="ts" setup>
172   import {getYMDHMS} from "@/utils/dateUtil"
173   import * as CategoryApi from "@/api/data/ind/category";
174   import * as DmModule from '@/api/model/pre/dm'
175   import * as ItemApi from "@/api/data/ind/item/item";
176   import * as MmPredictItem from '@/api/model/pre/predict'
177   import * as echarts from "echarts";
178   import { onMounted, ref } from 'vue';
179   import { Search, ArrowLeft, ArrowRight,} from '@element-plus/icons-vue'
180
181   defineOptions({name: 'AnalysisformData'})
182
183   const message = useMessage() // 消息弹窗
184   const { t } = useI18n() // 国际化
185   const dataCategoryList = ref([] as CategoryApi.IndItemCategoryVO[])
186   const loading1 = ref(false) // 列表的加载中
187   const loading2 = ref(false) // 列表的加载中
188   const total = ref(0) // 列表的总页数
189   const list = ref([]) // 字典表格数据
190   const queryParams = reactive({
191     pageNo: 1,
192     pageSize: 10,
193     itemno: '',
194     itemname: '',
195   })
196   let formData = ref({
197     rangeDate: '',
198     startTime: '',
199     endTime: '',
200     predictTime: '',
201     predictTimeStr: '',
202     startTimeStr: '',
203     endTimeStr: '',
204     predictTimeStamp: 0,
205     startTimeStamp: 0,
206     endTimeStamp: 0,
207     currentStamp: '',
208     currentStamp60: '',
209     predictStamp: '',
210     chartCheck: ['T+L', '真实值'],
211     chartOptions: ['T+N', 'T+L', '当时', '真实值', '调整值'],
212     checkedItemData: [],
213     backItem: '',
214     backValue: 0,
215     backCoe: 1,
216     preCumulant: 0,
217     realCumulant: 0,
218     queryStep: 2,
219     isMultipleYRadio: '单坐标轴',
220     isMultipleY: false,
221     predictFreq: 3,
222   })
223   let calRateForm = ref({
224     calItem: '',
225     IN_DEVIATION: 0,
226     OUT_DEVIATION: 0,
227     IN_ACCURACY_RATE: 0,
228     OUT_ACCURACY_RATE: 0,
229     itemAvg: 0,
230     itemMax: 0,
231     itemMin: 0,
232     itemPreAvg: 0,
233     itemPreMax: 0,
234     itemPreMin: 0,
235     preCumulant:0,
236     realCumulant:0
237   })
238   let itemData = ref({
239     currentTreeList: [],
240     chart: {},
241     option: {}
242   })
243   const chartContainer = ref(null);
244   const treeData = ref([])
245   const itemDataObject = ref()
246   const timer = ref()
f26b8f 247
J 248   const formRules = reactive({
249     calItem: [{required: true, message: '预测项不能为空', trigger: 'blur'}],
250     IN_DEVIATION: [{required: true, message: '精准度偏差不能为空', trigger: 'blur'}],
251     OUT_DEVIATION: [{required: true, message: '不可信率偏差不能为空', trigger: 'blur'}],
252   })
d7fad0 253   //const myChart = echarts.init(document.getElementById("data-analysis"));
J 254   /** 查询列表 */
255   const getList = async () => {
256     loading1.value = true
257     try {
258       const data = formData.value
259       if (!formData.value.chartCheck) {
260         formData.value.chartCheck = ['真实值']
261       }
262       let chartCheckArray = formData.value.chartCheck;
263       if (!formData.value.checkedItemData || formData.value.checkedItemData.length == 0) {
264         itemData.value.option = {};
265         return;
266       }
267       let itemIdList = formData.value.checkedItemData.map(item => {
268         return item.id
269       })
270       const params = ref({
271         itemIds: itemIdList.join(','),
272         predictTime: formData.value.predictTime,
273         startTime: formData.value.startTime,
274         endTime: formData.value.endTime
275       })
276       const res = await MmPredictItem.getViewCharts(params)
277       if (res.code !== 0) {
278         return message.error(res.msg)
279       }
280       formData.value.predictTime = res.data.predictTime;
281       formData.value.startTime = res.data.startTime
282       formData.value.endTime = res.data.endTime
283
284       let xAxisData = res.data.categories;
285       let yAxisData = [];
286       let offset = 0;
287       let yAxisIndex = 0;
288       let legendData = [];
289       let yMaxArr = [];
290       let seriesData = [];
291       seriesData.push({
292         name: '',
293         data: [null],
294         type: 'line',
295         smooth: true,
296         color: 'green',
297         markLine: {
298           silent: true,
299           lineStyle: {
300             color: '#32a487',
301             width: 2
302           },
303           data: [{
304             xAxis: formData.value.predictTime
305           }],
306           label: {
307             normal: {
308               formatter: formData.value.predictTime
309             }
310           },
311           symbol: ['circle', 'none'],
312         },
313       });
314       itemDataObject.value = {}
315       for (let i = 0; i < res.data.dataViewList.length; i++) {
316         let dataView = res.data.dataViewList[i]
317         itemDataObject.value.dataView.itemId = dataView;
318         let maxValue = dataView.maxValue;
319         let minValue = dataView.minValue;
320         yAxisIndex = formData.value.isMultipleY ? i : 0;
321         let yMax = maxValue;
322         if (maxValue < 0) {
323           maxValue = 1;
324         } else if (maxValue < 10) {
325           yMax = (Math.ceil(maxValue * 11) / 10).toFixed(1);
326         } else if (maxValue < 100) {
327           yMax = (Math.ceil(maxValue * 1.1 / 5) * 5);
328         } else {
329           yMax = (Math.ceil(maxValue * 1.1 / 10) * 10);
330         }
331         yMaxArr.push(yMax);
332         let yMin = minValue;
333         if (minValue >= 0) {
334           yMin = 0;
335         } else if (minValue > -10) {
336           yMin = (Math.floor(minValue * 11) / 10).toFixed(1);
337         } else if (minValue > -100) {
338           yMin = (Math.floor(minValue * 1.1 / 5) * 5);
339         } else {
340           yMin = (Math.floor(minValue * 1.1 / 10) * 10);
341         }
342         yAxisData.push({
343           type: 'value',
344           name: "",
345           min: yMin,
346           max: yMax,
347           position: 'left',
348           offset: offset,
349           splitLine: {
350             show: false
351           },
352           axisLine: {
353             show: true,
354             lineStyle: {}
355           },
356           axisLabel: {
357             formatter: '{value}'
358           }
359         })
360         offset = offset + 40
361         if (chartCheckArray.indexOf('真实值') !== -1) {
362           let legendName = dataView.itemName + '(真实)';
363           legendData.push(legendName);
364           seriesData.push({
365             name: legendName,
366             data: dataView.realData || [],
367             type: 'line',
368             yAxisIndex: yAxisIndex,
369             showSymbol: false,
370             smooth: true,
371             lineStyle: {
372               width: 3
373             }
374           });
375         }
376         if (chartCheckArray.indexOf('T+N') !== -1) {
377           let legendName = dataView.itemName + '(T+N)';
378           seriesData.push({
379             name: legendName,
380             data: dataView.preDataN || [],
381             type: 'line',
382             yAxisIndex: yAxisIndex,
383             showSymbol: false,
384             smooth: true,
385             lineStyle: {
386               width: 3
387             }
388           });
389         }
390         if (chartCheckArray.indexOf('T+L') !== -1) {
391           let legendName = dataView.itemName + '(T+L)';
392           legendData.push(legendName);
393           seriesData.push({
394             name: legendName,
395             data: dataView.preDataL || [],
396             type: 'line',
397             showSymbol: false,
398             connectNulls: true,
399             yAxisIndex: yAxisIndex,
400             smooth: true,
401             lineStyle: {
402               width: 3
403             }
404           });
405         }
406         if (chartCheckArray.indexOf('当时') !== -1) {
407           let legendName = dataView.itemName + '(当时)';
408           legendData.push(legendName);
409           seriesData.push({
410             name: legendName,
411             data: dataView.curData || [],
412             type: 'line',
413             yAxisIndex: yAxisIndex,
414             showSymbol: false,
415             smooth: true,
416             lineStyle: {
417               width: 3
418             }
419           });
420         }
421         if (chartCheckArray.indexOf('调整值') !== -1) {
422           let legendName = dataView.itemName + '(调整值)';
423           legendData.push(legendName);
424           seriesData.push({
425             name: legendName,
426             data: dataView.adjData || [],
427             type: 'line',
428             yAxisIndex: yAxisIndex,
429             showSymbol: false,
430             connectNulls: true,
431             smooth: true,
432             lineStyle: {
433               width: 3,
434               type: 'dashed'
435             }
436           });
437         }
438       }
439       //如果最大值相差不大,改成一致大小
440       if (yMaxArr.length > 1) {
441         let max = Math.max.apply(null, yMaxArr);
442         let min = Math.min.apply(null, yMaxArr);
443         if (Math.abs((max - min) / max) <= 0.2) {
444           for (let i = 0; i < yAxisData.length; i++) {
445             yAxisData[i].max = max;
446           }
447         }
448       }
449       let option = {
450         title: {
451           text: ''
452         },
453         tooltip: {
454           trigger: 'axis'
455         },
456         legend: {
457           show: true,
458           data: legendData,
459           top: 10
460         },
461         grid: {
462           top: 50,
463           left: '3%',
464           right: '6%',
465           bottom: '3%',
466           containLabel: true
467         },
468         xAxis: {
469           type: 'category',
470           boundaryGap: false,
471           data: xAxisData
472         },
473         yAxis: formData.value.isMultipleY ? yAxisData : {
474           type: 'value',
475           splitLine: {show: false},
476           axisLine: {show: true}
477         },
478         dataZoom: [
479           {
480             type: 'inside',
481             start: 0,
482             end: 100
483           },
484           {
485             start: 0,
486             end: 10
487           }
488         ],
489         series: seriesData
490       }
491       //chart.setOption(option)
492     } finally {
493       loading1.value = false
494     }
495   }
496
497   onMounted(() => {
498     getPreItemTree()
499     getList()
500   })
501
502   async function getPreItemTree() {
503     treeData.value = await MmPredictItem.getMmPredictItemTree()
504   }
505
506   function leftSearchDataByRange() {
507     let mins = getRangeMins();
508     let startTime = formData.value.startTime;
509     let endTime = formData.value.endTime;
510     let predictTime = formData.value.predictTime;
511     if (predictTime) {
512       predictTime = getYMDHMS(new Date(predictTime) - 1000 * 60 * mins);
513       formData.value.predictTime = predictTime;
514     }
515     if (startTime) {
516       startTime = getYMDHMS(new Date(startTime) - 1000 * 60 * mins);
517       formData.value.startTime = startTime;
518     }
519     if (endTime) {
520       endTime = getYMDHMS(new Date(endTime) - 1000 * 60 * mins);
521       formData.value.endTime = endTime;
522     }
523     getList();
524   }
525
526   function getRangeMins () {
527     let result: string | number = 0;
528     if(formData.value.startTime && formData.value.endTime) {
529       let startStamp = new Date(formData.value.startTime).getTime();
530       let endStamp = new Date(formData.value.endTime).getTime();
531       let queryStep = ((endStamp - startStamp) / (1000 * 60)).toFixed(0);
532       result = queryStep >= 0 ? queryStep : 0;
533     }
534     return result;
535   }
536
537   function onCheckTree(data, checked, indeterminate) {
538     formData.value.checkedItemData = [];
539     if (checked.checkedNodes) {
540       formData.value.checkedItemData = [...checked.checkedNodes]
541     }
542     //myChart.clear()
543     debounce(getList(), 1000);
544   }
545
546   function debounce(func, wait) {
547     let args = [];
548     if (timer.value) {
549       clearTimeout(timer.value);
550     }
551     timer.value = setTimeout(() => {
552       func.apply(this, args);
553       timer.value = null;
554     }, wait)
555   }
556
557   function calItemBaseVale() {
558     if (!calRateForm.value.calItem) {
559       calRateForm.value.itemPreMax = 0;
560       calRateForm.value.itemPreMin = 0;
561       calRateForm.value.itemPreAvg = 0;
562       calRateForm.value.preCumulant = 0;
563       calRateForm.value.itemMax = 0;
564       calRateForm.value.itemMin = 0;
565       calRateForm.value.itemAvg = 0;
566       calRateForm.value.realCumulant = 0;
567       return
568     } else {
569       let dataView = itemDataObject[calRateForm.value.calItem]
570       calRateForm.value.itemPreMax = dataView.preMax;
571       calRateForm.value.itemPreMin = dataView.preMin;
572       calRateForm.value.itemPreAvg = dataView.preAvg;
573       calRateForm.value.preCumulant = dataView.preCumulant;
574       calRateForm.value.itemMax = dataView.hisMax;
575       calRateForm.value.itemMin = dataView.hisMin;
576       calRateForm.value.itemAvg = dataView.hisAvg;
577       calRateForm.value.realCumulant = dataView.hisCumulant;
578     }
579   }
580
581   function calAccuracyRate() {
582     this.$refs['calRateForm'].validate((valid) => {
583       if (!valid) {
584         return false
585       }
586       let dataView = itemDataObject[calRateForm.value.calItem]
587       let seriesReaData = dataView.realData;
588       let seriesPreData = dataView.preDataL;
589       if (seriesReaData == null || seriesPreData == null ||
590         seriesReaData.length === 0 || seriesPreData.length === 0) {
591         loading2.value = false;
592         return;
593       }
594       let predictValueMap = {};
595       seriesPreData.forEach(function (item) {
596         predictValueMap[item[0]] = item[1];
597       })
598       let pointValueMap = {};
599       seriesReaData.forEach(function (item) {
600         pointValueMap[item[0]] = item[1];
601       })
602       let inDeviation = Number(calRateForm.value.IN_DEVIATION);
603       let outDeviation = Number(calRateForm.value.OUT_DEVIATION);
604       if (inDeviation === 0 && outDeviation === 0) {
605         loading2.value = false;
606         return;
607       }
608       let inDeviationCount = 0;
609       let outDeviationCount = 0;
610       let totalCount = 0;
611       for (let key in predictValueMap) {
612         let predictValue = predictValueMap[key];
613         let pointValue = pointValueMap[key];
614         if (pointValue == null || "" === pointValue || predictValue == null || "" === predictValue) {
615           continue;
616         }
617         let deviationAbs = (predictValue - pointValue) >= 0 ? (predictValue - pointValue) : (predictValue - pointValue) * -1;
618         if (deviationAbs < inDeviation) {
619           inDeviationCount = inDeviationCount + 1;
620         }
621         if (deviationAbs > outDeviation) {
622           outDeviationCount = outDeviationCount + 1;
623         }
624         totalCount = totalCount + 1;
625       }
626
627       let rateIn = (inDeviationCount / totalCount * 100).toFixed(2);
628       let rateOut = (outDeviationCount / totalCount * 100).toFixed(2);
629       calRateForm.value.IN_ACCURACY_RATE = Number(rateIn);
630       calRateForm.value.OUT_ACCURACY_RATE = Number(rateOut);
631       loading2.value = false;
632     })
633   }
634
635   function rightSearchDataByRange() {
636     let mins = getRangeMins();
637     let startTime = formData.value.startTime;
638     let endTime = formData.value.endTime;
639     let predictTime = formData.value.predictTime;
640     if (predictTime) {
641       predictTime = getYMDHMS(new Date(predictTime) - 0 + 1000 * 60 * mins);
642       formData.value.predictTime = predictTime;
643     }
644     if (startTime) {
645       startTime = getYMDHMS(new Date(startTime) - 0 + 1000 * 60 * mins);
646       formData.value.startTime = startTime;
647     }
648     if (endTime) {
649       endTime = getYMDHMS(new Date(endTime) - 0 + 1000 * 60 * mins);
650       formData.value.endTime = endTime;
651     }
652     getList();
653   }
654
655 </script>
656 <style scoped>
657   .el-form-item {
658     margin-bottom: 0 !important;
659   }
660
661   .his-body-chart {
662     height: 100%;
663     border: 1px solid lightgray;
664     padding: 10px;
665   }
666
667   .his-body-tree {
668     height: 100%;
669     border: 1px solid lightgray;
670     padding: 10px;
671   }
672
673   .his-body-right {
674     width: 80%;
675     height: 100%;
676     padding-top: 10px;
677   }
678
679   .his-body-left {
680     width: 20%;
681     height: 100%;
682     padding: 10px 10px 0 0;
683   }
684
685   .his-body {
686     width: 100%;
687     height: calc(calc(100vh - 48px - 38px - 130px));
688     display: flex;
689     flex-direction: row;
690     justify-content: flex-start;
691     align-content: flex-start;
692   }
693 </style>