From 0568fa140511a5df539dbc87759bb2040e7d8b10 Mon Sep 17 00:00:00 2001 From: houzhongjian <houzhongyi@126.com> Date: 星期二, 06 五月 2025 15:06:52 +0800 Subject: [PATCH] 模型管理数据分析页面增加授权功能,以供第三方嵌入 --- src/views/model/pre/analysis/index.vue | 900 +++++++++++++++++++++++++++++++++++++++++++++++++++-------- 1 files changed, 771 insertions(+), 129 deletions(-) diff --git a/src/views/model/pre/analysis/index.vue b/src/views/model/pre/analysis/index.vue index ef66aba..e377c25 100644 --- a/src/views/model/pre/analysis/index.vue +++ b/src/views/model/pre/analysis/index.vue @@ -1,7 +1,7 @@ <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 :inline="true" :model="formData" label-width="70px"> <el-form-item label="开始时间"> <el-date-picker v-model="formData.startTime" @@ -33,26 +33,44 @@ </el-form-item> <el-form-item> <el-button-group> - <el-button type="primary" plain :icon="ArrowLeft" + <el-button type="primary" plain :icon="DArrowLeft" :loading="loading1" @click="leftSearchDataByRange()"/> <el-button type="primary" plain :icon="Search" :loading="loading1" @click="getList()">查询 </el-button> - <el-button type="primary" plain :icon="ArrowRight" + <el-button type="primary" plain :icon="DArrowRight" :loading="loading1" @click="rightSearchDataByRange()"/> + </el-button-group> + </el-form-item> + <el-form-item> + <el-button-group> + <el-button type="primary" plain :icon="CaretLeft" + @click="playChart(true)"/> + <el-button type="primary" plain :icon="VideoPlay" v-if="!isPlay" + @click="playHandle('play')"/> + <el-button type="primary" plain :icon="VideoPause" v-if="isPlay" + @click="playHandle('pause')"/> + <el-button type="primary" plain :icon="CaretRight" + @click="playChart(false)"/> </el-button-group> </el-form-item> <div class="his-body"> <div class="his-body-left"> <div class="his-body-tree"> + <el-input + v-model="filterText" + class="mb-2" + placeholder="Filter" + /> <el-tree :data="treeData" show-checkbox node-key="id" - ref="tree" + ref="treeRef" highlight-current - :props="defaultProps" + check-strictly + :filter-node-method="filterNode" @check="onCheckTree"/> </div> </div> @@ -61,35 +79,23 @@ <el-form :inline="true" :model="calRateForm" :rules="formRules" ref="calRateFormRef" label-width="108px"> <el-row> - <el-col :span="6"> - <el-form-item label="预测项" prop="calItem" style="width: 90%"> - <el-select size="small" v-model="calRateForm.calItem" - @change="calItemBaseVale" - placeholder="请选择"> - <el-option - v-for="itemOut in formData.checkedItemData" - :key="itemOut.id" - :label="itemOut.label" - :value="itemOut.id"/> - </el-select> - </el-form-item> - </el-col> - <el-col :span="6"> +<!-- <el-col :span="6" style="display: flex;align-items: center;justify-content: center">--> +<!-- <span>预测项:{{formData.checkedItemData?.label || ''}}</span>--> +<!-- </el-col>--> + <el-col :span="8"> <el-form-item label="精准度偏差" prop="IN_DEVIATION"> <el-input-number size="small" v-model="calRateForm.IN_DEVIATION" - controls-position="right" :min="1" - :max="10"/> + controls-position="right" :min="0"/> </el-form-item> </el-col> - <el-col :span="6"> + <el-col :span="8"> <el-form-item label="不可信率偏差" prop="OUT_DEVIATION"> <el-input-number size="small" v-model="calRateForm.OUT_DEVIATION" controls-position="right" - :min="1" - :max="20"/> + :min="1"/> </el-form-item> </el-col> - <el-col :span="4"> + <el-col :span="8"> <el-form-item> <el-button size="small" type="primary" plain :loading="loading2" @click="calAccuracyRate">计算精准度 @@ -123,6 +129,11 @@ {{ calRateForm.preCumulant }} </el-form-item> </el-col> + <el-col :span="4"> + <el-form-item label="平均绝对误差:" label-width="110px"> + {{ calRateForm.deviation }} + </el-form-item> + </el-col> </el-row> <el-row> <el-col :span="4"> @@ -150,11 +161,16 @@ {{ calRateForm.realCumulant }} </el-form-item> </el-col> + <el-col :span="4"> + <el-form-item label="累积量平均绝对误差:" label-width="152px"> + {{ calRateForm.deviationCumulant }} + </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-col :span="18"> <el-form-item label="数据类型"> <el-checkbox-group v-model="formData.chartCheck" @change="changeChartCheck"> <el-checkbox v-for="item in formData.chartOptions" :label="item" @@ -175,7 +191,77 @@ </el-col> </el-row> </el-form> - <div ref="dataAnalysisChart" style="height: 500px;"></div> + <div style="width: 100%;height: 700px;display: flex;flex-direction: row;"> + <div style="height: 100%;width: 80%"> + <div ref="dataAnalysisChart" style="height: 50%;width: 100%"></div> + <div ref="influenceFactorChart" style="height: 50%;width: 100%"></div> + </div> + <div style="width: 20%;height: 100%;"> + <div style="display: flex;flex-direction: row;align-items: center;margin-bottom: 4px;"> + <div style="font-weight: bold;font-size: 14px">影响因素:</div> + <div style="width: calc(100% - 80px);"> + <el-select v-model="influenceFactor" placeholder="请选择" size="small" @change="changeInfluenceFactor"> + <el-option + v-for="(influenceFactor,index) in influenceFactorList" + :key="index" + :label="influenceFactor.factorOutputName" + :value="influenceFactor.factorOutputId" + /> + </el-select> + </div> + </div> + <div class="chart-foot-table p-2" style="width: 100%;height: calc(100% - 20px);overflow-x: hidden;overflow-y: auto;"> + <div style="display: flex;flex-direction: column;align-items: center;margin-bottom: 4px;"> + <span>影响时间</span> + <span style="font-size: 16px;font-weight: bold">{{influenceFactorResultTime}}</span> + </div> + <div v-for="(result, index) in influenceFactorResult" :key="index" style="display: flex;flex-direction: row"> + <span>{{result.factorOutputName}}:</span> + <span>{{result.value}}</span> + </div> + </div> + </div> + </div> + <div class="chart-foot"> + <div class="chart-foot-content"> + <h3 class="chart-foot-title">预警信息</h3> + <div class="chart-foot-table"> + <el-table :data="alarmList" style="width: 100%" v-loading="loadingAlarm" height="100px"> + <el-table-column prop="content" header-align="center" align="left" label="消息内容" min-width="240" /> + <el-table-column prop="alarmType" label="预警类型" header-align="center" align="left" min-width="150"/> + <el-table-column prop="alarmTime" label="预警时间" header-align="center" align="left" min-width="150"/> + </el-table> + </div> + </div> + <div class="chart-foot-content"> + <h3 class="chart-foot-title">调度建议</h3> + <div class="chart-foot-table"> + <el-table :data="suggestList" style="width: 100%" v-loading="loadingAdjust" height="100px"> + <el-table-column + prop="scheduleTime" + label="调度时间" + header-align="center" + align="left" + min-width="160" + /> + <el-table-column + prop="content" + label="内容" + min-width="300" + header-align="center" align="left" + /> + <el-table-column + prop="adjustValue" + label="调整值" + header-align="center" + align="center" + min-width="100" + /> + </el-table> + </div> + + </div> + </div> </div> </div> </div> @@ -184,16 +270,20 @@ </el-card> </template> <script lang="ts" setup> -import {getYMDHMS} from "@/utils/dateUtil" +import {getYMDHMS,formatToDateTime} from "@/utils/dateUtil" import * as McsApi from '@/api/model/mcs' +import * as influenceFactorApi from '@/api/model/pre/influenceFactor/influenceFactorApi' +import * as AlarmMessageApi from '@/api/model/pre/alarm/message' +import * as ScheSuggestApi from '@/api/model/sche/suggest' import * as echarts from "echarts"; -import {Search, ArrowLeft, ArrowRight,} from '@element-plus/icons-vue' +import {Search, DArrowLeft, DArrowRight, VideoPlay, VideoPause, CaretLeft, CaretRight} from '@element-plus/icons-vue' +import {lighten} from "@/utils/color"; defineOptions({name: 'AnalysisformData'}) const message = useMessage() // 消息弹窗 const {t} = useI18n() // 国际化 -const dataAnalysisChart = ref(null); + const loading1 = ref(false) // 列表的加载中 const loading2 = ref(false) // 列表的加载中 const total = ref(0) // 列表的总页数 @@ -213,8 +303,8 @@ currentStamp60: '', predictStamp: '', chartCheck: ['T+L', '真实值'], - chartOptions: ['T+N', 'T+L', '当时', '真实值', '调整值'], - checkedItemData: [], + chartOptions: ['T+N', 'T+L','T+L(未调整)', '当时', '真实值', '调整值', '预测累计', '真实累计'], + checkedItemData: undefined, backItem: '', backValue: 0, backCoe: 1, @@ -223,13 +313,13 @@ queryStep: 2, isMultipleYRadio: '单坐标轴', isMultipleY: false, - predictFreq: 3, + predictFreq: 2, }) const calRateFormRef = ref() const calRateForm = ref({ calItem: undefined, - IN_DEVIATION: 0, - OUT_DEVIATION: 0, + IN_DEVIATION: 10, + OUT_DEVIATION: 50, IN_ACCURACY_RATE: 0, OUT_ACCURACY_RATE: 0, itemAvg: 0, @@ -239,7 +329,9 @@ itemPreMax: 0, itemPreMin: 0, preCumulant: 0, - realCumulant: 0 + realCumulant: 0, + deviation: 0, //平均绝对误差 + deviationCumulant: 0, //累积量平均绝对误差 }) let itemData = ref({ currentTreeList: [], @@ -249,41 +341,114 @@ const treeData = ref([]) const itemDataObject = ref() const timer = ref() -let myChart = null; +const isPlay = ref(false) +const alarmList = ref([]) +const suggestList = ref([]) +const loadingAlarm = ref(false) +const loadingAdjust = ref(false) + +// 影响因素结果列表 +const influenceFactorResultList = ref([]) +// 影响因素列表 +const influenceFactorList = ref([]) +// 选中影响因素 +const influenceFactor = ref() const formRules = reactive({ - calItem: [{required: true, message: '预测项不能为空', trigger: 'blur'}], IN_DEVIATION: [{required: true, message: '精准度偏差不能为空', trigger: 'blur'}], OUT_DEVIATION: [{required: true, message: '不可信率偏差不能为空', trigger: 'blur'}], }) +// 树形过滤 +const filterText = ref('') +const treeRef = ref() +watch(filterText, (val) => { + treeRef.value!.filter(val) +}) +const filterNode = (value: string, data) => { + if (!value) return true + return data.label.includes(value) +} + +let xAxisData = [] + /** 查询列表 */ -const getList = async () => { +const getList = async (isClear = true) => { loading1.value = true try { if (!formData.value.chartCheck) { formData.value.chartCheck = ['真实值'] } let chartCheckArray = formData.value.chartCheck; - if (!formData.value.checkedItemData || formData.value.checkedItemData.length == 0) { + if (!formData.value.checkedItemData) { itemData.value.option = {}; return; } - let outIds = formData.value.checkedItemData.map(item => { - return item.id - }) + let outIds = [formData.value.checkedItemData.id] const params = reactive({ outIds: outIds, predictTime: formData.value.predictTime, startTime: formData.value.startTime, endTime: formData.value.endTime }) + + const data = await McsApi.getPreDataCharts(params) formData.value.predictTime = data.predictTime; formData.value.startTime = data.startTime formData.value.endTime = data.endTime - let xAxisData = data.categories; + // 默认影响时间 + changeInfluenceFactorTime(data.predictTime); + + // 获取影响因素结果列表 + influenceFactorResultList.value = await influenceFactorApi.getResultList({ + outIds: outIds, + startTime: data.startTime, + endTime: data.endTime + }) + + // 获取影响因素结果列表 + influenceFactorList.value = await influenceFactorApi.getListByOutId(formData.value.checkedItemData.id) + if (influenceFactorList.value && influenceFactorList.value.length > 0) { + // 根据factorOutputId去重,因为不同的统计规则会有重复的影响因素 + influenceFactorList.value = Array.from(new Map(influenceFactorList.value.map(item => [item.factorOutputId, item])).values()); + // 默认选中第一个影响因素 + influenceFactor.value = influenceFactorList.value?.[0]?.factorOutputId + getInfluenceFactorChart(influenceFactorList.value?.[0]?.factorOutputId) + } + + + const paramsAlarm = reactive({ + outIds: outIds, + predictTime: formData.value.predictTime + }) + + loadingAlarm.value = true + alarmList.value = await AlarmMessageApi.getListByOut(paramsAlarm) + loadingAlarm.value = false + + loadingAdjust.value = true + suggestList.value = await ScheSuggestApi.getListByOut(paramsAlarm) + loadingAdjust.value = false + + xAxisData = data.categories; + let defaultYAxis = [ + { + type: 'value', + name: "累计值", + splitLine: {show: false}, + axisLine: {show: true}, + position: 'right' + }, + { + type: 'value', + name: "", + splitLine: {show: false}, + axisLine: {show: true}, + position: 'left' + } + ]; let yAxisData = []; let offset = 0; let yAxisIndex = 0; @@ -314,12 +479,27 @@ }, }); itemDataObject.value = {} + yAxisData.push({ + type: 'value', + name: "累计值", + position: 'right', + splitLine: { + show: false + }, + axisLine: { + show: true, + lineStyle: {} + }, + axisLabel: { + formatter: '{value}' + } + }) for (let i = 0; i < data.dataViewList.length; i++) { let dataView = data.dataViewList[i] itemDataObject.value[dataView.outId] = dataView; let maxValue = dataView.maxValue; let minValue = dataView.minValue; - yAxisIndex = formData.value.isMultipleY ? i : 0; + yAxisIndex = (formData.value.isMultipleY ? i : 0) + 1; let yMax = maxValue; if (maxValue < 0) { maxValue = 1; @@ -369,7 +549,7 @@ type: 'line', yAxisIndex: yAxisIndex, showSymbol: false, - smooth: true, + smooth: false, lineStyle: { width: 2 } @@ -377,13 +557,14 @@ } if (chartCheckArray.indexOf('T+N') !== -1) { let legendName = dataView.resultName + '(T+N)'; + legendData.push(legendName); seriesData.push({ name: legendName, data: dataView.preDataN || [], type: 'line', yAxisIndex: yAxisIndex, showSymbol: false, - smooth: true, + smooth: false, lineStyle: { width: 2 } @@ -399,7 +580,24 @@ showSymbol: false, connectNulls: true, yAxisIndex: yAxisIndex, - smooth: true, + smooth: false, + lineStyle: { + width: 2 + } + }); + } + if (chartCheckArray.indexOf('T+L(未调整)') !== -1) { + let legendName = dataView.resultName + '(T+L(未调整))'; + legendData.push(legendName); + seriesData.push({ + name: legendName, + data: dataView.preDataLOriginal + || [], + type: 'line', + showSymbol: false, + connectNulls: true, + yAxisIndex: yAxisIndex, + smooth: false, lineStyle: { width: 2 } @@ -413,10 +611,10 @@ data: dataView.curData || [], type: 'line', yAxisIndex: yAxisIndex, - showSymbol: false, - smooth: true, + showSymbol: true, + smooth: false, lineStyle: { - width: 2 + width: 3 } }); } @@ -430,7 +628,51 @@ yAxisIndex: yAxisIndex, showSymbol: false, connectNulls: true, - smooth: true, + smooth: false, + lineStyle: { + width: 2, + type: 'dashed' + } + }); + } + + if (chartCheckArray.indexOf('预测累计') !== -1) { + let legendName = dataView.resultName + '(预测累计)'; + legendData.push(legendName); + let seriesLeiJiData = [] + if (dataView.cumulantPreData) { + seriesLeiJiData = dataView.cumulantPreData + } + seriesData.push({ + name: legendName, + data: seriesLeiJiData, + type: 'line', + yAxisIndex: 0, + showSymbol: false, + connectNulls: true, + smooth: false, + lineStyle: { + width: 2, + type: 'dashed' + } + }); + } + + if (chartCheckArray.indexOf('真实累计') !== -1) { + let legendName = dataView.resultName + '(真实累计)'; + legendData.push(legendName); + let seriesLeiJiData = [] + if (dataView.cumulantRealData) { + seriesLeiJiData = dataView.cumulantRealData + } + seriesData.push({ + name: legendName, + data: seriesLeiJiData, + type: 'line', + yAxisIndex: 0, + showSymbol: false, + connectNulls: true, + smooth: false, lineStyle: { width: 2, type: 'dashed' @@ -448,13 +690,19 @@ } } } - myChart = echarts.init(dataAnalysisChart.value); + let option = { title: { text: '' }, tooltip: { trigger: 'axis' + }, + toolbox: { + show: true, + feature: { + saveAsImage: {} + } }, legend: { show: true, @@ -463,8 +711,8 @@ }, grid: { top: '20%', - left: '3%', - right: '6%', + left: '5%', + right: '5%', bottom: '3%', containLabel: true }, @@ -473,11 +721,7 @@ boundaryGap: false, data: xAxisData }, - yAxis: formData.value.isMultipleY ? yAxisData : { - type: 'value', - splitLine: {show: false}, - axisLine: {show: true} - }, + yAxis: formData.value.isMultipleY ? yAxisData : defaultYAxis, dataZoom: [ { type: 'inside', @@ -491,20 +735,366 @@ ], series: seriesData } - myChart.clear() + if (isClear) { + myChart.clear() + } + myChart.setOption(option) + + + + + } finally { + loading1.value = false + } + + calItemBaseVale() +} + +// 查询影响因素chart +const getInfluenceFactorChart = async (outId) => { + loading1.value = true + try { + + let outIds = [outId] + const params = reactive({ + outIds: outIds, + predictTime: formData.value.predictTime, + startTime: formData.value.startTime, + endTime: formData.value.endTime + }) + + const data = await McsApi.getPreDataCharts(params) + + if (!data?.dataViewList || data.dataViewList.length === 0) { + myInfluenceFactorChart.clear() + return + } + + xAxisData = data.categories; + let defaultYAxis = [ + { + type: 'value', + name: "累计值", + splitLine: {show: false}, + axisLine: {show: true}, + position: 'right' + }, + { + type: 'value', + name: "", + splitLine: {show: false}, + axisLine: {show: true}, + position: 'left' + } + ]; + let yAxisData = []; + let offset = 0; + let yAxisIndex = 0; + let legendData = []; + let yMaxArr = []; + let seriesData = []; + seriesData.push({ + name: '', + data: [null], + type: 'line', + smooth: true, + color: 'green', + markLine: { + silent: true, + lineStyle: { + color: '#32a487', + width: 2 + }, + data: [{ + xAxis: formData.value.predictTime + }], + label: { + normal: { + formatter: formData.value.predictTime + } + }, + symbol: ['circle', 'none'], + }, + }); + itemDataObject.value = {} + yAxisData.push({ + type: 'value', + name: "累计值", + position: 'right', + splitLine: { + show: false + }, + axisLine: { + show: true, + lineStyle: {} + }, + axisLabel: { + formatter: '{value}' + } + }) + for (let i = 0; i < data.dataViewList.length; i++) { + let dataView = data.dataViewList[i] + itemDataObject.value[dataView.outId] = dataView; + let maxValue = dataView.maxValue; + let minValue = dataView.minValue; + yAxisIndex = (formData.value.isMultipleY ? i : 0) + 1; + let yMax = maxValue; + if (maxValue < 0) { + maxValue = 1; + } else if (maxValue < 10) { + yMax = (Math.ceil(maxValue * 11) / 10).toFixed(1); + } else if (maxValue < 100) { + yMax = (Math.ceil(maxValue * 1.1 / 5) * 5); + } else { + yMax = (Math.ceil(maxValue * 1.1 / 10) * 10); + } + yMaxArr.push(yMax); + let yMin = minValue; + if (minValue >= 0) { + yMin = 0; + } else if (minValue > -10) { + yMin = (Math.floor(minValue * 11) / 10).toFixed(1); + } else if (minValue > -100) { + yMin = (Math.floor(minValue * 1.1 / 5) * 5); + } else { + yMin = (Math.floor(minValue * 1.1 / 10) * 10); + } + yAxisData.push({ + type: 'value', + name: "", + min: yMin, + max: yMax, + position: 'left', + offset: offset, + splitLine: { + show: false + }, + axisLine: { + show: true, + lineStyle: {} + }, + axisLabel: { + formatter: '{value}' + } + }) + offset = offset + 40 + //真实值 + legendData.push(dataView.resultName + '(真实)'); + seriesData.push({ + name: dataView.resultName + '(真实)', + data: dataView.realData || [], + type: 'line', + yAxisIndex: yAxisIndex, + showSymbol: false, + smooth: false, + lineStyle: { + width: 2 + } + }); + //T+L + legendData.push(dataView.resultName + '(T+L)'); + seriesData.push({ + name: dataView.resultName + '(T+L)', + data: dataView.preDataL || [], + type: 'line', + showSymbol: false, + connectNulls: true, + yAxisIndex: yAxisIndex, + smooth: false, + lineStyle: { + width: 2 + } + }); + // 当时 + legendData.push(dataView.resultName + '(当时)'); + seriesData.push({ + name: dataView.resultName + '(当时)', + data: dataView.curData || [], + type: 'line', + yAxisIndex: yAxisIndex, + showSymbol: true, + smooth: false, + lineStyle: { + width: 3 + } + }); + //预测累计 + legendData.push(dataView.resultName + '(预测累计)'); + seriesData.push({ + name: dataView.resultName + '(预测累计)', + data: dataView.cumulantPreData || [], + type: 'line', + yAxisIndex: 0, + showSymbol: false, + connectNulls: true, + smooth: false, + lineStyle: { + width: 2, + type: 'dashed' + } + }); + // 真实累计 + legendData.push(dataView.resultName + '(真实累计)'); + seriesData.push({ + name: dataView.resultName + '(真实累计)', + data: dataView.cumulantRealData || [], + type: 'line', + yAxisIndex: 0, + showSymbol: false, + connectNulls: true, + smooth: false, + lineStyle: { + width: 2, + type: 'dashed' + } + }); + } + //如果最大值相差不大,改成一致大小 + if (yMaxArr.length > 1) { + let max = Math.max.apply(null, yMaxArr); + let min = Math.min.apply(null, yMaxArr); + if (Math.abs((max - min) / max) <= 0.2) { + for (let i = 0; i < yAxisData.length; i++) { + yAxisData[i].max = max; + } + } + } + + let option = { + title: { + text: '' + }, + tooltip: { + trigger: 'axis' + }, + toolbox: { + show: true, + feature: { + saveAsImage: {} + } + }, + legend: { + show: true, + data: legendData, + top: 10 + }, + grid: { + top: '20%', + left: '5%', + right: '5%', + bottom: '3%', + containLabel: true + }, + xAxis: { + type: 'category', + boundaryGap: false, + data: xAxisData + }, + yAxis: formData.value.isMultipleY ? yAxisData : defaultYAxis, + dataZoom: [ + { + type: 'inside', + start: 0, + end: 100 + }, + { + start: 0, + end: 10 + } + ], + series: seriesData + } + myInfluenceFactorChart.clear() + myInfluenceFactorChart.setOption(option) } finally { loading1.value = false } } + + onMounted(() => { + initChart() resetForm() getPreItemTree() }) +const dataAnalysisChart = ref(); +const influenceFactorChart = ref(); +let myChart = ref({}); +let myInfluenceFactorChart = ref({}); + +function initChart() { + myChart = echarts.init(dataAnalysisChart.value) + myInfluenceFactorChart = echarts.init(influenceFactorChart.value) + // 监听点击事件 + myChart.getZr().on('click', 'series.line',function (params) { + var pointInPixel = [params.offsetX, params.offsetY]; + var pointInData = myChart.convertFromPixel('grid', pointInPixel); + const time = xAxisData[pointInData[0]]; + changeInfluenceFactorTime(time) + }); +} + +let influenceFactorResult = ref([]) +let influenceFactorResultTime = ref('') +// 影响因素时间改变 +function changeInfluenceFactorTime(time) { + if (time && new Date(time)?.getTime()) { + influenceFactorResultTime.value = time + influenceFactorResult.value = influenceFactorResultList.value?.[formData.value.checkedItemData?.id]?.filter(e => e.time === new Date(time).getTime()).sort((a, b) => b.value - a.value) || []; + } +}// 选择影响因素 +function changeInfluenceFactor(value) { + getInfluenceFactorChart(value) +} + async function getPreItemTree() { treeData.value = await McsApi.getPredictItemTree() +} + +function changeChartCheck(value) { + getList(true) +} + +function onChangeMultipleY() { + getList(true) +} + +function playChart(isBack = false) { + let mins = isBack ? formData.value.predictFreq * -1 : formData.value.predictFreq + let startTime = formData.value.startTime; + let endTime = formData.value.endTime; + let predictTime = formData.value.predictTime; + if (predictTime) { + predictTime = getYMDHMS(new Date(predictTime).getTime() + 1000 * 60 * mins); + formData.value.predictTime = predictTime; + } + if (startTime) { + startTime = getYMDHMS(new Date(startTime).getTime() + 1000 * 60 * mins); + formData.value.startTime = startTime; + } + if (endTime) { + endTime = getYMDHMS(new Date(endTime).getTime() + 1000 * 60 * mins); + formData.value.endTime = endTime; + } + getList(false); +} + +function playHandle(type) { + isPlay.value = 'play' === type + let doPlay = setInterval(function () { + if (isPlay.value) { + playChart() + } else { + clearInterval(doPlay); + } + if (new Date().getTime() - new Date(formData.value.predictTime).getTime() < 1000 * 60 ) { + isPlay.value = false + clearInterval(doPlay); + } + }, 1000) } function leftSearchDataByRange() { @@ -513,18 +1103,18 @@ let endTime = formData.value.endTime; let predictTime = formData.value.predictTime; if (predictTime) { - predictTime = getYMDHMS(new Date(predictTime) - 1000 * 60 * mins); + predictTime = getYMDHMS(new Date(predictTime).getTime() - 1000 * 60 * mins); formData.value.predictTime = predictTime; } if (startTime) { - startTime = getYMDHMS(new Date(startTime) - 1000 * 60 * mins); + startTime = getYMDHMS(new Date(startTime).getTime() - 1000 * 60 * mins); formData.value.startTime = startTime; } if (endTime) { - endTime = getYMDHMS(new Date(endTime) - 1000 * 60 * mins); + endTime = getYMDHMS(new Date(endTime).getTime() - 1000 * 60 * mins); formData.value.endTime = endTime; } - getList(); + getList(false); } function getRangeMins() { @@ -539,10 +1129,21 @@ } function onCheckTree(data, checked, indeterminate) { - formData.value.checkedItemData = []; - if (checked.checkedNodes) { - formData.value.checkedItemData = [...checked.checkedNodes] - } + // 单选 + treeRef.value.setCheckedKeys([]) + treeRef.value.setCheckedNodes([data]) + + formData.value.checkedItemData = data + calRateForm.value.calItem = data.id + // if (checked.checkedNodes) { + // let cns = [...checked.checkedNodes] + // for (let i = 0; i < cns.length; i++) { + // if (cns[i].id.indexOf('-') !== -1) { + // continue + // } + // formData.value.checkedItemData.push(cns[i]) + // } + // } debounce(getList, 1000); } @@ -577,61 +1178,80 @@ calRateForm.value.itemMin = dataView.hisMin; calRateForm.value.itemAvg = dataView.hisAvg; calRateForm.value.realCumulant = dataView.hisCumulant; + calDeviation(dataView.realData,dataView.preDataL,'deviation') + calDeviation(dataView.cumulantRealData,dataView.cumulantPreData,'deviationCumulant') + calAccuracyRate() } } 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; - } + const valid = calRateFormRef.value.validate() + if (!valid) return - 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); + let dataView = itemDataObject.value[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 calDeviation(realData,preDataL,key) { + if (realData == null || preDataL == null || realData.length === 0 || preDataL.length === 0) { + return; + } + const realObj = {} + realData.map(e => realObj[e[0]] = e[1]) + + let sum = 0; + let index = 0; + preDataL.forEach(e => { + if (realObj[e[0]] != undefined) { + sum += Math.abs(e[1] - realObj[e[0]]) + index++ + } + }) + calRateForm.value[key] = Number((sum / index).toFixed(2)) } function rightSearchDataByRange() { @@ -640,18 +1260,18 @@ let endTime = formData.value.endTime; let predictTime = formData.value.predictTime; if (predictTime) { - predictTime = getYMDHMS(new Date(predictTime) - 0 + 1000 * 60 * mins); + predictTime = getYMDHMS(new Date(predictTime).getTime() + 1000 * 60 * mins); formData.value.predictTime = predictTime; } if (startTime) { - startTime = getYMDHMS(new Date(startTime) - 0 + 1000 * 60 * mins); + startTime = getYMDHMS(new Date(startTime).getTime() + 1000 * 60 * mins); formData.value.startTime = startTime; } if (endTime) { - endTime = getYMDHMS(new Date(endTime) - 0 + 1000 * 60 * mins); + endTime = getYMDHMS(new Date(endTime).getTime() + 1000 * 60 * mins); formData.value.endTime = endTime; } - getList(); + getList(false); } /** 重置表单 */ @@ -671,8 +1291,8 @@ currentStamp60: '', predictStamp: '', chartCheck: ['T+L', '真实值'], - chartOptions: ['T+N', 'T+L', '当时', '真实值', '调整值'], - checkedItemData: [], + chartOptions: ['T+N', 'T+L','T+L(未调整)', '当时', '真实值', '调整值', '预测累计', '真实累计'], + checkedItemData: undefined, backItem: '', backValue: 0, backCoe: 1, @@ -681,12 +1301,12 @@ queryStep: 2, isMultipleYRadio: '单坐标轴', isMultipleY: false, - predictFreq: 3, + predictFreq: 2, } calRateForm.value = { calItem: undefined, - IN_DEVIATION: 0, - OUT_DEVIATION: 0, + IN_DEVIATION: 10, + OUT_DEVIATION: 50, IN_ACCURACY_RATE: 0, OUT_ACCURACY_RATE: 0, itemAvg: 0, @@ -696,12 +1316,31 @@ itemPreMax: 0, itemPreMin: 0, preCumulant: 0, - realCumulant: 0 + realCumulant: 0, + deviation: 0, //平均绝对误差 + deviationCumulant: 0, //累积量平均绝对误差 } calRateFormRef.value?.resetFields() } </script> <style scoped> +.chart-foot-table { + border: 1px solid #bababa; +} +.chart-foot-title { + font-size: 14px; +} +.chart-foot-content { + height: 100%; + width: 50%; + padding: 5px; +} +.chart-foot { + height: 120px; + width: 100%; + display: flex; + flex-direction: row; +} .el-form-item { margin-bottom: 0 !important; } @@ -710,12 +1349,15 @@ height: 100%; border: 1px solid lightgray; padding: 10px; + overflow-y: auto; + overflow-x: hidden; } .his-body-tree { height: 100%; border: 1px solid lightgray; - padding: 10px; + padding: 10px 10px 20px 10px; + overflow-y: auto; } .his-body-right { @@ -732,7 +1374,7 @@ .his-body { width: 100%; - height: calc(calc(100vh - 68px - 38px - 160px)); + height: calc(calc(100vh - 68px - 38px - 60px)); display: flex; flex-direction: row; justify-content: flex-start; -- Gitblit v1.9.3