|
|
|
|
@ -13,7 +13,7 @@ import {
|
|
|
|
|
Tag,
|
|
|
|
|
} from 'ant-design-vue';
|
|
|
|
|
import * as echarts from 'echarts';
|
|
|
|
|
|
|
|
|
|
import { getSensorHistoryData } from '#/api/hazard/sensorData';
|
|
|
|
|
import { deviceInfo } from '#/api/hazard/device';
|
|
|
|
|
import { sensorQuery } from '#/api/hazard/sensor';
|
|
|
|
|
import { useNotifyStore } from '#/store/notify';
|
|
|
|
|
@ -70,39 +70,50 @@ const updateChart = (message: any) => {
|
|
|
|
|
switch (message.type) {
|
|
|
|
|
case '0': {
|
|
|
|
|
chartKey = `temperature-${message.sensorId}`;
|
|
|
|
|
console.log('更新温度图表:', chartKey);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case '1': {
|
|
|
|
|
chartKey = `humidity-${message.sensorId}`;
|
|
|
|
|
console.log('更新湿度图表:', chartKey);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case '2': {
|
|
|
|
|
chartKey = `pressure-${message.sensorId}`;
|
|
|
|
|
console.log('更新压力图表:', chartKey);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
// No default
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const chart = chartInstances.value[chartKey];
|
|
|
|
|
console.log('更新图表:', chart);
|
|
|
|
|
if (chart) {
|
|
|
|
|
const option = {
|
|
|
|
|
if (chart && chartDataBuffer.value[chartKey] && xchartDataBuffer.value[chartKey]) {
|
|
|
|
|
// 更新数据缓冲区
|
|
|
|
|
const buffer = chartDataBuffer.value[chartKey];
|
|
|
|
|
const xBuffer = xchartDataBuffer.value[chartKey];
|
|
|
|
|
|
|
|
|
|
// 添加新的数据点
|
|
|
|
|
buffer.push(Number.parseFloat(message.value));
|
|
|
|
|
buffer.shift(); // 移除最旧的数据点
|
|
|
|
|
|
|
|
|
|
// 添加新的时间点
|
|
|
|
|
const currentTime = new Date().toTimeString().slice(0, 8); // HH:MM:SS格式
|
|
|
|
|
xBuffer.push(currentTime);
|
|
|
|
|
xBuffer.shift(); // 移除最旧的时间点
|
|
|
|
|
|
|
|
|
|
// 更新图表显示
|
|
|
|
|
const displayData = buffer.slice(-360);
|
|
|
|
|
const displayXData = xBuffer.slice(-360);
|
|
|
|
|
|
|
|
|
|
chart.setOption({
|
|
|
|
|
|
|
|
|
|
xAxis: {
|
|
|
|
|
data: displayXData
|
|
|
|
|
},
|
|
|
|
|
series: [
|
|
|
|
|
{
|
|
|
|
|
data: [
|
|
|
|
|
{
|
|
|
|
|
value: message.value,
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
data: displayData,
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
};
|
|
|
|
|
chart.setOption(option);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 计算属性 - 获取各类传感器列表
|
|
|
|
|
const temperatureSensors = computed(() => {
|
|
|
|
|
return getSensorsByType('0');
|
|
|
|
|
@ -163,83 +174,93 @@ const createChart = (chartKey: string, container: HTMLElement) => {
|
|
|
|
|
return chart;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const fetchHistoryData = async (sensorId: string) => {
|
|
|
|
|
try {
|
|
|
|
|
// 调用获取历史数据的API,获取近1小时的360条数据
|
|
|
|
|
const historyData = await getSensorHistoryData({sensorCode: sensorId});
|
|
|
|
|
console.log('获取到的历史数据:', historyData.rows);
|
|
|
|
|
return historyData.rows; // 返回获取到的历史数据
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('获取历史数据失败:', error);
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
// 在组件顶部添加数据缓冲区
|
|
|
|
|
const chartDataBuffer = ref<Record<string, number[]>>({});
|
|
|
|
|
const xchartDataBuffer = ref<Record<string, Date[]>>({});
|
|
|
|
|
// 渲染图表
|
|
|
|
|
const renderChart = (chart: any, type: string, sensor: any, index: number) => {
|
|
|
|
|
const renderChart = async (chart: any, type: string, sensor: any, index: number) => {
|
|
|
|
|
let option;
|
|
|
|
|
const chartKey = `${type}-${sensor.id}`;
|
|
|
|
|
const dataLength = 360; // 1小时内360条数据(每10秒一条)
|
|
|
|
|
|
|
|
|
|
// 获取历史数据
|
|
|
|
|
const historyData = await fetchHistoryData(sensor.id);
|
|
|
|
|
// 初始化数据缓冲区
|
|
|
|
|
if (!chartDataBuffer.value[chartKey]) {
|
|
|
|
|
chartDataBuffer.value[chartKey] = new Array(dataLength).fill(0);
|
|
|
|
|
}
|
|
|
|
|
if (historyData && historyData.length > 0) {
|
|
|
|
|
// 处理历史数据,提取value值
|
|
|
|
|
const values = historyData.map((item: any) => Number.parseFloat(item.value) || 0);
|
|
|
|
|
const xvalues = historyData.map((item: any) => {
|
|
|
|
|
const date = new Date(item.time);
|
|
|
|
|
return date.toTimeString().slice(0, 8); // 格式化为 HH:MM:SS
|
|
|
|
|
// 或者只显示时分: date.toTimeString().substr(0, 5) // 格式化为 HH:MM
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
chartDataBuffer.value[chartKey] = values;
|
|
|
|
|
xchartDataBuffer.value[chartKey] = xvalues;
|
|
|
|
|
}
|
|
|
|
|
// 生成时间标签(显示12个时间点作为参考)
|
|
|
|
|
const xAxisData = ref([]);
|
|
|
|
|
// for (let i = 0; i < 12; i++) {
|
|
|
|
|
// const minutesAgo = (11 - i) * 5; // 每5分钟一个标签点
|
|
|
|
|
// const time = new Date(Date.now() - minutesAgo * 60 * 1000);
|
|
|
|
|
// xAxisData.push(time.toTimeString().substr(0, 5));
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
// 使用缓冲区中的所有数据
|
|
|
|
|
const seriesData = chartDataBuffer.value[chartKey];
|
|
|
|
|
xAxisData.value = xchartDataBuffer.value[chartKey];
|
|
|
|
|
console.log('seriesData', seriesData);
|
|
|
|
|
console.log('xAxisData', xAxisData.value);
|
|
|
|
|
switch (type) {
|
|
|
|
|
case 'humidity': {
|
|
|
|
|
option = {
|
|
|
|
|
backgroundColor: 'transparent',
|
|
|
|
|
// title: {
|
|
|
|
|
// text: sensor.name || `湿度${index + 1}`,
|
|
|
|
|
// left: 'center',
|
|
|
|
|
// },
|
|
|
|
|
tooltip: {
|
|
|
|
|
trigger: 'axis',
|
|
|
|
|
axisPointer: {
|
|
|
|
|
type: 'cross',
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
xAxis: {
|
|
|
|
|
type: 'category',
|
|
|
|
|
data: xAxisData.value,
|
|
|
|
|
},
|
|
|
|
|
yAxis: {
|
|
|
|
|
type: 'value',
|
|
|
|
|
name: '湿度(%)',
|
|
|
|
|
},
|
|
|
|
|
series: [
|
|
|
|
|
{
|
|
|
|
|
type: 'gauge',
|
|
|
|
|
center: ['50%', '60%'],
|
|
|
|
|
startAngle: 180,
|
|
|
|
|
endAngle: 0,
|
|
|
|
|
min: 0,
|
|
|
|
|
max: deviceData.value.maxHumidity || 100,
|
|
|
|
|
splitNumber: 10,
|
|
|
|
|
radius: '90%',
|
|
|
|
|
axisLine: {
|
|
|
|
|
lineStyle: {
|
|
|
|
|
width: 10,
|
|
|
|
|
color: [
|
|
|
|
|
[0.3, '#a0cfff'],
|
|
|
|
|
[0.7, '#40a9ff'],
|
|
|
|
|
[1, '#2f54eb'],
|
|
|
|
|
],
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
pointer: {
|
|
|
|
|
icon: 'path://M2.9,0.7L2.9,0.7c1.4,0,2.6,1.2,2.6,2.6v115c0,1.4-1.2,2.6-2.6,2.6l0,0c-1.4,0-2.6-1.2-2.6-2.6V3.3C0.3,1.9,1.4,0.7,2.9,0.7z',
|
|
|
|
|
length: '70%',
|
|
|
|
|
width: 3,
|
|
|
|
|
offsetCenter: [0, '-30%'],
|
|
|
|
|
},
|
|
|
|
|
axisTick: {
|
|
|
|
|
show: true,
|
|
|
|
|
length: 8,
|
|
|
|
|
lineStyle: {
|
|
|
|
|
color: 'auto',
|
|
|
|
|
width: 2,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
splitLine: {
|
|
|
|
|
show: true,
|
|
|
|
|
length: 15,
|
|
|
|
|
lineStyle: {
|
|
|
|
|
color: 'auto',
|
|
|
|
|
width: 3,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
axisLabel: {
|
|
|
|
|
show: true,
|
|
|
|
|
distance: 20,
|
|
|
|
|
fontSize: 12,
|
|
|
|
|
},
|
|
|
|
|
detail: {
|
|
|
|
|
show: true,
|
|
|
|
|
formatter: '{value}%',
|
|
|
|
|
fontSize: 16,
|
|
|
|
|
offsetCenter: [0, '40%'],
|
|
|
|
|
color: '#333',
|
|
|
|
|
data: seriesData,
|
|
|
|
|
type: 'line',
|
|
|
|
|
smooth: true,
|
|
|
|
|
symbol: 'none',
|
|
|
|
|
symbolSize: 6,
|
|
|
|
|
lineStyle: {
|
|
|
|
|
color: '#40a9ff',
|
|
|
|
|
width: 2,
|
|
|
|
|
},
|
|
|
|
|
title: {
|
|
|
|
|
show: true,
|
|
|
|
|
offsetCenter: [0, '80%'],
|
|
|
|
|
fontSize: 12,
|
|
|
|
|
color: '#666',
|
|
|
|
|
itemStyle: {
|
|
|
|
|
color: '#40a9ff',
|
|
|
|
|
},
|
|
|
|
|
data: [
|
|
|
|
|
{
|
|
|
|
|
value:
|
|
|
|
|
deviceData.value[`humidity${index}`] ||
|
|
|
|
|
deviceData.value.humidity ||
|
|
|
|
|
0,
|
|
|
|
|
name: sensor.name || `湿度${index + 1}`,
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
};
|
|
|
|
|
@ -249,75 +270,38 @@ const renderChart = (chart: any, type: string, sensor: any, index: number) => {
|
|
|
|
|
case 'pressure': {
|
|
|
|
|
option = {
|
|
|
|
|
backgroundColor: 'transparent',
|
|
|
|
|
// title: {
|
|
|
|
|
// text: sensor.name || `压力${index + 1}`,
|
|
|
|
|
// left: 'center',
|
|
|
|
|
// },
|
|
|
|
|
tooltip: {
|
|
|
|
|
trigger: 'axis',
|
|
|
|
|
axisPointer: {
|
|
|
|
|
type: 'cross',
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
xAxis: {
|
|
|
|
|
type: 'category',
|
|
|
|
|
data: xAxisData.value,
|
|
|
|
|
},
|
|
|
|
|
yAxis: {
|
|
|
|
|
type: 'value',
|
|
|
|
|
name: '压力(kPa)',
|
|
|
|
|
},
|
|
|
|
|
series: [
|
|
|
|
|
{
|
|
|
|
|
type: 'gauge',
|
|
|
|
|
center: ['50%', '60%'],
|
|
|
|
|
startAngle: 180,
|
|
|
|
|
endAngle: 0,
|
|
|
|
|
min: 0,
|
|
|
|
|
max: deviceData.value.maxPressure || 100,
|
|
|
|
|
splitNumber: 10,
|
|
|
|
|
radius: '90%',
|
|
|
|
|
axisLine: {
|
|
|
|
|
lineStyle: {
|
|
|
|
|
width: 10,
|
|
|
|
|
color: [
|
|
|
|
|
[0.3, '#ffd666'],
|
|
|
|
|
[0.7, '#ffa940'],
|
|
|
|
|
[1, '#ff4d4f'],
|
|
|
|
|
],
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
pointer: {
|
|
|
|
|
icon: 'path://M2.9,0.7L2.9,0.7c1.4,0,2.6,1.2,2.6,2.6v115c0,1.4-1.2,2.6-2.6,2.6l0,0c-1.4,0-2.6-1.2-2.6-2.6V3.3C0.3,1.9,1.4,0.7,2.9,0.7z',
|
|
|
|
|
length: '70%',
|
|
|
|
|
width: 3,
|
|
|
|
|
offsetCenter: [0, '-30%'],
|
|
|
|
|
},
|
|
|
|
|
axisTick: {
|
|
|
|
|
show: true,
|
|
|
|
|
length: 8,
|
|
|
|
|
lineStyle: {
|
|
|
|
|
color: 'auto',
|
|
|
|
|
width: 2,
|
|
|
|
|
},
|
|
|
|
|
data: seriesData,
|
|
|
|
|
type: 'line',
|
|
|
|
|
smooth: true,
|
|
|
|
|
symbol: 'none',
|
|
|
|
|
symbolSize: 6,
|
|
|
|
|
lineStyle: {
|
|
|
|
|
color: '#ffa940',
|
|
|
|
|
width: 2,
|
|
|
|
|
},
|
|
|
|
|
splitLine: {
|
|
|
|
|
show: true,
|
|
|
|
|
length: 15,
|
|
|
|
|
lineStyle: {
|
|
|
|
|
color: 'auto',
|
|
|
|
|
width: 3,
|
|
|
|
|
},
|
|
|
|
|
itemStyle: {
|
|
|
|
|
color: '#ffa940',
|
|
|
|
|
},
|
|
|
|
|
axisLabel: {
|
|
|
|
|
show: true,
|
|
|
|
|
distance: 20,
|
|
|
|
|
fontSize: 12,
|
|
|
|
|
},
|
|
|
|
|
detail: {
|
|
|
|
|
show: true,
|
|
|
|
|
formatter: '{value}kPa',
|
|
|
|
|
fontSize: 16,
|
|
|
|
|
offsetCenter: [0, '40%'],
|
|
|
|
|
color: '#333',
|
|
|
|
|
},
|
|
|
|
|
title: {
|
|
|
|
|
show: true,
|
|
|
|
|
offsetCenter: [0, '80%'],
|
|
|
|
|
fontSize: 12,
|
|
|
|
|
color: '#666',
|
|
|
|
|
},
|
|
|
|
|
data: [
|
|
|
|
|
{
|
|
|
|
|
value:
|
|
|
|
|
deviceData.value[`pressure${index}`] ||
|
|
|
|
|
deviceData.value.pressure ||
|
|
|
|
|
0,
|
|
|
|
|
name: sensor.name || `压力${index + 1}`,
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
};
|
|
|
|
|
@ -327,75 +311,38 @@ const renderChart = (chart: any, type: string, sensor: any, index: number) => {
|
|
|
|
|
case 'temperature': {
|
|
|
|
|
option = {
|
|
|
|
|
backgroundColor: 'transparent',
|
|
|
|
|
// title: {
|
|
|
|
|
// text: sensor.name || `温度${index + 1}`,
|
|
|
|
|
// left: 'center',
|
|
|
|
|
// },
|
|
|
|
|
tooltip: {
|
|
|
|
|
trigger: 'axis',
|
|
|
|
|
axisPointer: {
|
|
|
|
|
type: 'cross',
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
xAxis: {
|
|
|
|
|
type: 'category',
|
|
|
|
|
data: xAxisData.value,
|
|
|
|
|
},
|
|
|
|
|
yAxis: {
|
|
|
|
|
type: 'value',
|
|
|
|
|
name: '温度(°C)',
|
|
|
|
|
},
|
|
|
|
|
series: [
|
|
|
|
|
{
|
|
|
|
|
type: 'gauge',
|
|
|
|
|
center: ['50%', '60%'],
|
|
|
|
|
startAngle: 180,
|
|
|
|
|
endAngle: 0,
|
|
|
|
|
min: -20,
|
|
|
|
|
max: deviceData.value.maxTemp || 100,
|
|
|
|
|
splitNumber: 10,
|
|
|
|
|
radius: '90%',
|
|
|
|
|
axisLine: {
|
|
|
|
|
lineStyle: {
|
|
|
|
|
width: 10,
|
|
|
|
|
color: [
|
|
|
|
|
[0.3, '#67e0e3'],
|
|
|
|
|
[0.7, '#37a2da'],
|
|
|
|
|
[1, '#fd666d'],
|
|
|
|
|
],
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
pointer: {
|
|
|
|
|
icon: 'path://M2.9,0.7L2.9,0.7c1.4,0,2.6,1.2,2.6,2.6v115c0,1.4-1.2,2.6-2.6,2.6l0,0c-1.4,0-2.6-1.2-2.6-2.6V3.3C0.3,1.9,1.4,0.7,2.9,0.7z',
|
|
|
|
|
length: '70%',
|
|
|
|
|
width: 3,
|
|
|
|
|
offsetCenter: [0, '-30%'],
|
|
|
|
|
},
|
|
|
|
|
axisTick: {
|
|
|
|
|
show: true,
|
|
|
|
|
length: 8,
|
|
|
|
|
lineStyle: {
|
|
|
|
|
color: 'auto',
|
|
|
|
|
width: 2,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
splitLine: {
|
|
|
|
|
show: true,
|
|
|
|
|
length: 15,
|
|
|
|
|
lineStyle: {
|
|
|
|
|
color: 'auto',
|
|
|
|
|
width: 3,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
axisLabel: {
|
|
|
|
|
show: true,
|
|
|
|
|
distance: 20,
|
|
|
|
|
fontSize: 12,
|
|
|
|
|
},
|
|
|
|
|
detail: {
|
|
|
|
|
show: true,
|
|
|
|
|
formatter: '{value}°C',
|
|
|
|
|
fontSize: 16,
|
|
|
|
|
offsetCenter: [0, '40%'],
|
|
|
|
|
color: '#333',
|
|
|
|
|
data: seriesData,
|
|
|
|
|
type: 'line',
|
|
|
|
|
smooth: true,
|
|
|
|
|
symbol: 'none',
|
|
|
|
|
symbolSize: 6,
|
|
|
|
|
lineStyle: {
|
|
|
|
|
color: '#37a2da',
|
|
|
|
|
width: 2,
|
|
|
|
|
},
|
|
|
|
|
title: {
|
|
|
|
|
show: true,
|
|
|
|
|
offsetCenter: [0, '80%'],
|
|
|
|
|
fontSize: 12,
|
|
|
|
|
color: '#666',
|
|
|
|
|
itemStyle: {
|
|
|
|
|
color: '#37a2da',
|
|
|
|
|
},
|
|
|
|
|
data: [
|
|
|
|
|
{
|
|
|
|
|
value:
|
|
|
|
|
deviceData.value[`temperature${index}`] ||
|
|
|
|
|
deviceData.value.temperature ||
|
|
|
|
|
0,
|
|
|
|
|
name: sensor.name || `温度${index + 1}`,
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
};
|
|
|
|
|
@ -418,7 +365,7 @@ const setChartRef = (el: HTMLElement | null, chartKey: string) => {
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 初始化所有图表
|
|
|
|
|
const initAllCharts = () => {
|
|
|
|
|
const initAllCharts = async () => {
|
|
|
|
|
// 确保 DOM 已经渲染完成
|
|
|
|
|
nextTick(() => {
|
|
|
|
|
// 温度传感器图表
|
|
|
|
|
@ -510,6 +457,7 @@ onUnmounted(() => {
|
|
|
|
|
window.removeEventListener('resize', handleResize);
|
|
|
|
|
// 清理图表实例
|
|
|
|
|
cleanupCharts();
|
|
|
|
|
chartDataBuffer.value = {};
|
|
|
|
|
});
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
@ -560,23 +508,7 @@ onUnmounted(() => {
|
|
|
|
|
{{ deviceData.remark || '-' }}
|
|
|
|
|
</DescriptionsItem>
|
|
|
|
|
</Descriptions>
|
|
|
|
|
</TabPane>
|
|
|
|
|
|
|
|
|
|
<TabPane key="status" tab="运行状态">
|
|
|
|
|
<div class="status-dashboard">
|
|
|
|
|
<div class="device-status-bar status-bar-hover">
|
|
|
|
|
<div class="status-item">
|
|
|
|
|
<span class="label">设备连接🔗状态:</span>
|
|
|
|
|
<Tag :color="deviceData.status === '1' ? 'green' : 'red'">
|
|
|
|
|
{{ deviceData.status === '1' ? '在线' : '离线' }}
|
|
|
|
|
</Tag>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="status-item">
|
|
|
|
|
<span class="label">设备名称:</span>
|
|
|
|
|
<span>{{ deviceData.name }}</span>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="chart-grid">
|
|
|
|
|
<!-- 温度传感器仪表盘 -->
|
|
|
|
|
<div
|
|
|
|
|
@ -690,7 +622,7 @@ onUnmounted(() => {
|
|
|
|
|
|
|
|
|
|
.chart-grid {
|
|
|
|
|
display: grid;
|
|
|
|
|
grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
|
|
|
|
|
//grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
|
|
|
|
|
gap: 24px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -716,7 +648,7 @@ onUnmounted(() => {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.chart-container {
|
|
|
|
|
height: 280px;
|
|
|
|
|
height: 320px;
|
|
|
|
|
width: 100%;
|
|
|
|
|
position: relative;
|
|
|
|
|
}
|
|
|
|
|
|