<!-- 趋势预警 -->
|
<template>
|
<div class="flex-column" :style="`height: ${page_height}; background-color: transparent`" v-if="state.showViewDetail">
|
<div>
|
<el-form
|
style="width: 100%; height: calc(100% - 50px); background-color: #fff"
|
:model="state.editTrendFunFrom"
|
label-width="80px"
|
>
|
<el-row :gutter="20">
|
<!-- <el-col :span="8">
|
<el-form-item label="泵站">
|
<el-input v-model="state.editTrendFunFrom.Group" autocomplete="off"></el-input>
|
</el-form-item>
|
</el-col>
|
<el-col :span="8">
|
<el-form-item label="测点">
|
<el-input v-model="state.editTrendFunFrom.MonitorPoint" autocomplete="off"></el-input>
|
</el-form-item>
|
</el-col>
|
<el-col :span="8">
|
<el-form-item label="信号">
|
<el-input v-model="state.editTrendFunFrom.Signal" autocomplete="off"></el-input>
|
</el-form-item>
|
</el-col>
|
</el-row>
|
<el-row :gutter="20">
|
<el-col :span="8">
|
<el-form-item label="阈值">
|
<el-input v-model="state.editTrendFunFrom.ThresholdValue" autocomplete="off"></el-input>
|
</el-form-item>
|
</el-col>
|
<el-col :span="8">
|
<el-form-item label="单位">
|
<el-input v-model="state.editTrendFunFrom.UnitName" filterable autocomplete="off"></el-input>
|
</el-form-item>
|
</el-col> -->
|
<el-col :span="8">
|
<el-form-item label="名称">
|
<el-input v-model="state.editTrendFunFrom.Name" filterable autocomplete="off" readonly></el-input>
|
</el-form-item>
|
</el-col>
|
<el-col :span="8">
|
<el-form-item label="方法">
|
<el-input v-model="state.editTrendFunFrom.MethodName" filterable autocomplete="off" readonly></el-input>
|
</el-form-item>
|
</el-col>
|
<el-col :span="8">
|
<el-form-item label="方向">
|
<el-input v-model="state.editTrendFunFrom.Direction" autocomplete="off" readonly></el-input>
|
</el-form-item>
|
</el-col>
|
</el-row>
|
<el-row :gutter="20">
|
<el-col :span="8">
|
<el-form-item label="历史天数">
|
<el-input v-model="state.editTrendFunFrom.PastTime" autocomplete="off" readonly></el-input>
|
</el-form-item>
|
</el-col>
|
<el-col :span="8">
|
<el-form-item label="历史步长">
|
<el-input v-model="state.editTrendFunFrom.PastStep" autocomplete="off" readonly></el-input>
|
</el-form-item>
|
</el-col>
|
<el-col :span="8">
|
<el-form-item label="预测天数">
|
<el-input v-model="state.editTrendFunFrom.FutureTime" autocomplete="off" readonly></el-input>
|
</el-form-item>
|
</el-col>
|
</el-row>
|
<el-row :gutter="20">
|
<el-col :span="8">
|
<el-form-item label="发生时间">
|
<el-input v-model="state.editTrendFunFrom.HappenTime" autocomplete="off" readonly></el-input>
|
</el-form-item>
|
</el-col>
|
<el-col :span="8">
|
<el-form-item label="来源方式">
|
<el-input v-model="state.editTrendFunFrom.SourceWay" autocomplete="off" readonly></el-input>
|
</el-form-item>
|
</el-col>
|
<el-col :span="8">
|
<el-form-item label="分析人">
|
<el-input v-model="state.editTrendFunFrom.CreateUserName" autocomplete="off" readonly></el-input>
|
</el-form-item>
|
</el-col>
|
</el-row>
|
<el-row :gutter="20">
|
<el-col :span="8">
|
<el-form-item label="分析时间">
|
<el-input v-model="state.editTrendFunFrom.PredictTime" autocomplete="off" readonly></el-input>
|
</el-form-item>
|
</el-col>
|
<el-col :span="8">
|
<el-form-item label="阈值">
|
<el-input v-model="state.editTrendFunFrom.ThresholdValue" autocomplete="off" readonly></el-input>
|
</el-form-item>
|
</el-col>
|
</el-row>
|
<el-form-item label="说明">
|
<el-input type="textarea" :rows="2" autocomplete="off" v-model="state.editTrendFunFrom.Description" readonly> </el-input>
|
</el-form-item>
|
</el-form>
|
</div>
|
<div class="dialogBottom flex-auto overflow-hidden">
|
<div class="dialogBottomLeft h100 flex-column">
|
<titleBoxVue style="background-color: #fff" title="历史数据图"> </titleBoxVue>
|
<div class="flex-auto overflow-hidden">
|
<div :id="ChartDomIds.leftChart" class="w100 h100"></div>
|
</div>
|
</div>
|
</div>
|
</div>
|
</template>
|
|
<script lang="ts" setup>
|
import titleBoxVue from '/@/components/titleBox.vue';
|
import { reactive, onMounted, ref, nextTick, computed, watch, PropType, toRefs, shallowRef } from 'vue';
|
import * as echarts from 'echarts';
|
import { ChartDomIds } from '/@/views/types/echartDetail';
|
import { ThresholdDirectionEnum } from '/@/projectCom/trend/types';
|
import { SourceWayEnum } from '/@/views/types';
|
import { ElMessage } from 'element-plus';
|
let m_chartMain = shallowRef(null);
|
const props = defineProps({
|
editTrendFunFrom: Object,
|
leftChartData: Array<any>,
|
page_height: {
|
type: String,
|
default: '70vh',
|
},
|
});
|
const { editTrendFunFrom, leftChartData, page_height } = toRefs(props);
|
// 定义变量内容
|
const state = reactive({
|
titleName: '有效值-时序预警模型列表',
|
startDate: '',
|
endDate: '',
|
addTrendFunVisible: false,
|
editTrendFunFrom: {
|
Name: '',
|
Group: '',
|
MonitorPoint: '',
|
Signal: '',
|
ThresholdValue: '',
|
UnitName: '',
|
PastTime: '',
|
PastStep: '',
|
PredictTime: '',
|
FutureTime: '',
|
Direction: '',
|
HappenTime: '',
|
CreateUserName: '',
|
AlarmValue: '',
|
Description: '',
|
SourceWay: '',
|
MethodName: '',
|
} as any,
|
chartData: {
|
PastRecordList: [],
|
FutureRecordList: [],
|
},
|
activeIndex: 0,
|
dateRange: [] as any, //选中的日期
|
showViewDetail: true, //是否展示详情
|
_chartValueRecord: '',
|
pointMax: '' as any, //向上取第一个最大点
|
m_chartOption: {} as any,
|
});
|
onMounted(() => {
|
window.addEventListener('resize', () => {
|
setTimeout(() => {
|
m_chartMain.value && m_chartMain.value.resize();
|
}, 10);
|
});
|
});
|
watch(editTrendFunFrom, (newVal, oldVal) => {
|
state.editTrendFunFrom = {
|
Group: newVal.Group,
|
MonitorPoint: newVal.MonitorPoint,
|
Signal: newVal.Signal,
|
ThresholdValue: newVal.ThresholdValue + newVal.UnitName,
|
UnitName: newVal.UnitName,
|
PastTime: newVal.PastTime,
|
PastStep: newVal.PastStep,
|
PredictTime: newVal.PredictTime,
|
FutureTime: newVal.FutureTime,
|
Direction: newVal.Direction === ThresholdDirectionEnum.Up ? '向上' : '向下',
|
HappenTime: newVal.HappenTime,
|
CreateUserName: newVal.CreateUserName,
|
Description: newVal.Description,
|
AlarmValue: newVal.AlarmValue,
|
Name: newVal.Name,
|
SourceWay: SourceWayEnum[newVal.SourceWay], //来源方式
|
MethodName: newVal.MethodName,
|
};
|
if (newVal.PastRecordList == null) return ElMessage.warning('暂无过去的数据');
|
state.chartData.PastRecordList = newVal.PastRecordList;
|
state.chartData.FutureRecordList = newVal.FutureRecordList;
|
let time = state.editTrendFunFrom.PredictTime; //获取分析时间
|
//插入过去时间
|
let pastData_i = getLastDateTime(state.chartData.PastRecordList, time);
|
let pastData = state.chartData.PastRecordList;
|
let past_node = {
|
DataTime: time,
|
DataValue: null,
|
};
|
pastData.splice(pastData_i, 0, past_node); //插入一条离分析时间最近的数据
|
//插入未来数据
|
let futrueData_i = getLastDateTime(state.chartData.FutureRecordList, time);
|
let futrueData = state.chartData.FutureRecordList;
|
let node = {
|
DataTime: time,
|
DataValue: null,
|
};
|
futrueData.splice(futrueData_i, 0, node); //插入一条离分析时间最近的数据
|
setTimeout(() => {
|
initChartLeft();
|
}, 10);
|
});
|
//获取最近的一条时间
|
const getLastDateTime = (arr: any, time: any) => {
|
let currenTime: any = new Date(time);
|
let min = 0;
|
let i = 0;
|
arr.forEach((item, index) => {
|
let itemTime: any = new Date(item.DataTime);
|
let InternalTime = Math.abs(currenTime - itemTime);
|
if (min == 0) min = InternalTime;
|
|
if (InternalTime < min) {
|
min = InternalTime;
|
i = index;
|
}
|
});
|
let lastTime: any = new Date(arr[i].DataTime);
|
if (currenTime - lastTime > 0) {
|
i = i + 1;
|
}
|
return i;
|
};
|
//选择是日月年还是自定义
|
const payChangeHandle = (i, item) => {
|
state.activeIndex = i;
|
};
|
// 选中日历日期后会执行的回调!!
|
// 主要用这个方法获取到用户选择的初始时间,然后在禁用方法里通过这个时间设置结束时间的禁用时间
|
const calendarChange = (dates: any) => {
|
let hasSelectDate = dates !== null && dates.length > 0;
|
state.dateRange = hasSelectDate ? dates[0] : null;
|
};
|
const disabledDate = (time: any) => {
|
const timeRange = 1 * 24 * 60 * 60 * 1000; // 1天时间戳
|
const tempTime = Date.now() - timeRange;
|
if (state.dateRange) {
|
// console.log('有开始时间了', state.dateRange)
|
const minTime = new Date(state.dateRange).getTime();
|
const maxTime = new Date(state.dateRange).getTime() + timeRange * 365;
|
if (tempTime < maxTime) {
|
return time.getTime() < minTime || time.getTime() > tempTime;
|
}
|
return time.getTime() < minTime || time.getTime() > maxTime;
|
} else {
|
return time.getTime() >= tempTime;
|
}
|
};
|
|
//点击日期切换
|
const dateChanged = () => {
|
payChangeHandle(3, { id: 4 });
|
};
|
//初始化历史数据图
|
const initChartLeft = () => {
|
if (m_chartMain.value) {
|
m_chartMain.value.clear();
|
}
|
m_chartMain.value = echarts.init(document.getElementById(ChartDomIds.leftChart));
|
const dataValue = state.chartData.PastRecordList.map((v) => {
|
let history_time = v.DataTime;
|
let history_value = v.DataValue;
|
return {
|
value: [history_time, history_value],
|
};
|
});
|
const futureValue = state.chartData.FutureRecordList.map((v) => {
|
let future_time = v.DataTime;
|
let future_value = v.DataValue;
|
return {
|
value: [future_time, future_value],
|
};
|
});
|
var color_curve = '#4169E1';
|
let legendList = ['历史数据', '预测数据'];
|
let series_arr = [];
|
series_arr.push(
|
{
|
type: 'line',
|
showSymbol: false,
|
name: legendList[0],
|
emphasis: {
|
scale: false,
|
},
|
data: dataValue,
|
lineStyle: {
|
color: color_curve,
|
},
|
},
|
{
|
type: 'line',
|
showSymbol: false,
|
name: legendList[1],
|
emphasis: {
|
scale: false,
|
},
|
data: futureValue,
|
lineStyle: {
|
color: 'red',
|
},
|
itemStyle: {
|
color: 'red',
|
},
|
}
|
);
|
var main_chart_option = {
|
title: {
|
text: '', //主标题
|
},
|
legend: {
|
right: '6%',
|
textStyle: {
|
color: 'black', //图例文字
|
},
|
},
|
tooltip: {
|
trigger: 'axis',
|
|
axisPointer: {
|
animation: false,
|
},
|
},
|
grid: [
|
{
|
top: 40,
|
right: 60,
|
left: 50,
|
bottom: 60,
|
containLabel: true,
|
},
|
],
|
toolbox: {
|
show: true,
|
top: 0,
|
right: 40,
|
itemSize: 20,
|
iconStyle: {
|
color: '#87CEFA', //设置颜色
|
},
|
},
|
dataZoom: [
|
{
|
id: 'dataZoomX',
|
type: 'slider',
|
xAxisIndex: [0],
|
start: 0,
|
end: 100,
|
},
|
{
|
// 这个dataZoom组件,也控制x轴。
|
type: 'inside', // 这个 dataZoom 组件是 inside 型 dataZoom 组件
|
start: 0, // 左边在 24% 的位置。
|
end: 100, // 右边在 100% 的位置。
|
},
|
],
|
visualMap: {
|
show: false,
|
dimension: 1,
|
pieces: [
|
{
|
gte: 0.1,
|
lte: 0.7,
|
color: '#0BD2C8',
|
},
|
],
|
},
|
xAxis: {
|
type: 'category',
|
splitLine: {
|
show: true,
|
},
|
axisPointer: {
|
type: 'shadow',
|
},
|
splitNumber: 8,
|
minInterval: 1,
|
minorTick: {
|
show: false,
|
splitNumber: 2,
|
},
|
minorSplitLine: {
|
show: true,
|
lineStyle: {
|
color: '#aaa',
|
type: 'dashed',
|
},
|
},
|
},
|
yAxis: {
|
nameLocation: 'middle',
|
nameGap: 48,
|
type: 'value',
|
// axisLabel: {
|
// formatter: '{value} ',
|
// },
|
// scale: true,
|
boundaryGap: ['0%', '35%'],
|
splitLine: {
|
show: true,
|
},
|
nameTextStyle: {
|
color: color_curve,
|
},
|
// 整条y轴
|
axisLine: {
|
show: true,
|
},
|
// y轴上的小横线
|
axisTick: {
|
show: true,
|
},
|
},
|
|
series: series_arr,
|
};
|
state.m_chartOption = main_chart_option;
|
m_chartMain.value.setOption(main_chart_option);
|
if (state.editTrendFunFrom.HappenTime) {
|
getFutureWarn();
|
}
|
if (state.editTrendFunFrom.PredictTime) {
|
getFuturePredictTime();
|
}
|
};
|
//预测报警值
|
const getFutureWarn = () => {
|
if (state.chartData.FutureRecordList.length == 0) return;
|
let makePoint_common = [];
|
// state.chartData.FutureRecordList.forEach((item) => {
|
// if (state.editTrendFunFrom.PredictTime && item.DataTime > state.editTrendFunFrom.PredictTime) {
|
// if (ThresholdDirectionEnum.Up) {
|
// if (state.editTrendFunFrom.ThresholdValue < item.DataValue) {
|
// let pointObj = {
|
// value: item.DataValue,
|
// coord: [item.DataTime, item.DataValue],
|
// };
|
// makePoint_common.push(pointObj);
|
// }
|
// }
|
// if (ThresholdDirectionEnum.Down) {
|
// if (state.editTrendFunFrom.ThresholdValue > item.DataValue) {
|
// let pointObj = {
|
// value: item.DataValue,
|
// coord: [item.DataTime, item.DataValue],
|
// };
|
// makePoint_common.push(pointObj);
|
// }
|
// }
|
// }
|
// });
|
let pointObj = {
|
value: state.editTrendFunFrom.AlarmValue,
|
coord: [state.editTrendFunFrom.HappenTime, state.editTrendFunFrom.AlarmValue],
|
};
|
makePoint_common.push(pointObj);
|
if (makePoint_common.length == 0) return ElMessage.warning('暂无报警值');
|
//获取阀值最高/最低点
|
state.pointMax = makePoint_common[0];
|
let _markPoint = {
|
symbol: 'pin',
|
data: [state.pointMax],
|
itemStyle: {
|
color: '#91cc75', // 设置 markPoint 的颜色为绿色
|
},
|
};
|
state.m_chartOption.series[1].markPoint = _markPoint;
|
setTimeout(() => {
|
m_chartMain.value.setOption(state.m_chartOption); //将_markPoint在option的值重新赋值
|
}, 500);
|
};
|
//预测分析时间
|
const getFuturePredictTime = () => {
|
if (state.chartData.FutureRecordList.length == 0) return;
|
//获取Y轴的刻度范围
|
var rangeY = m_chartMain.value.getModel().getComponent('yAxis').axis.scale._extent;
|
let _markLine = {
|
//盒须图样式。
|
label: {
|
formatter: `分析时间\n${state.editTrendFunFrom.PredictTime}`,
|
color: 'red',
|
distance: [0, 10],
|
},
|
|
data: [
|
[
|
{
|
coord: [state.editTrendFunFrom.PredictTime, 0],
|
},
|
{
|
coord: [state.editTrendFunFrom.PredictTime, rangeY[1]],
|
},
|
],
|
],
|
};
|
state.m_chartOption.series[1].markLine = _markLine;
|
setTimeout(() => {
|
m_chartMain.value.setOption(state.m_chartOption); //将_markPoint在option的值重新赋值
|
}, 500);
|
};
|
</script>
|
<style scoped lang="scss">
|
.boxright {
|
flex: 1;
|
height: 100%;
|
overflow: auto;
|
background: transparent;
|
margin-left: 20px;
|
}
|
|
:deep(.el-form .el-form-item:last-of-type) {
|
margin: 10px 10px 10px 0px !important;
|
}
|
.dialogBottom {
|
// display: flex;
|
// align-items: center;
|
// justify-content: space-between;
|
margin-top: 10px;
|
}
|
.dialogBottomLeft {
|
width: 100%;
|
// height: 493px;
|
background-color: #fff;
|
}
|
.dialogBottomRight {
|
width: calc(50% - 10px);
|
height: 300px;
|
margin-left: 10px;
|
background-color: #fff;
|
}
|
.btn-group {
|
width: 240px;
|
height: 30px;
|
display: flex;
|
|
> span {
|
cursor: pointer;
|
flex: 1;
|
text-align: center;
|
line-height: 30px;
|
border-radius: 2px;
|
border-top: 1px solid #ccc;
|
border-bottom: 1px solid #ccc;
|
border-right: 1px solid #ccc;
|
&:first-child {
|
border-left: 1px solid #ccc;
|
}
|
&.active {
|
border-color: rgb(9, 202, 16);
|
background-color: rgb(9, 202, 16);
|
color: #fff;
|
}
|
}
|
|
:deep(.el-date-editor) {
|
width: 240px;
|
height: 36px;
|
margin-left: 16px;
|
border-color: #ccc;
|
}
|
.date-input {
|
width: 146px;
|
height: 40px;
|
}
|
.pay-charts {
|
margin-top: 15px;
|
width: 100%;
|
height: 400px;
|
}
|
|
.empty-con2 {
|
height: 471px;
|
}
|
}
|
</style>
|