<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" :rules="formRules" ref="calRateForm" label-width="108px">
|
<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/item'
|
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 formRules = reactive({
|
calItem: [{required: true, message: '预测项不能为空', trigger: 'blur'}],
|
IN_DEVIATION: [{required: true, message: '精准度偏差不能为空', trigger: 'blur'}],
|
OUT_DEVIATION: [{required: true, message: '不可信率偏差不能为空', trigger: 'blur'}],
|
})
|
//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 - 68px - 38px - 160px));
|
display: flex;
|
flex-direction: row;
|
justify-content: flex-start;
|
align-content: flex-start;
|
}
|
</style>
|