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