设备传感器数据折线图表示,展示一小时内的数据

master
yangxiaozhong 1 month ago
parent 7aa2b3eedc
commit d1af213a50

@ -15,6 +15,10 @@ export function sensorDataList(params?: SensorDataQuery) {
return requestClient.get<PageResult<SensorDataVO>>('/hazard/sensorData/list', { params });
}
export function getSensorHistoryData(params?: SensorDataQuery) {
return requestClient.get<PageResult<SensorDataVO>>('/hazard/sensorData/history', { params });
}
/**
*
* @param params

@ -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 {
// API1360
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; // 136010
//
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;
}

@ -150,7 +150,8 @@ onMounted(async () => {
</script>
<template>
<Layout>
<div>
<Layout>
<Layout.Content class="page-content">
<!-- 搜索条件区域 -->
<div class="search-container">
@ -239,7 +240,8 @@ onMounted(async () => {
</div>
</Layout.Content>
</Layout>
<DeviceModal @reload="handleReset()" />
<DeviceModal @reload="handleReset()" />
</div>
</template>
<style scoped>

@ -6,7 +6,7 @@ import { DictEnum } from '@vben/constants';
import { $t } from '@vben/locales';
import { cloneDeep } from '@vben/utils';
import { Badge, Descriptions, DescriptionsItem, Divider } from 'ant-design-vue';
import { Badge, Descriptions, DescriptionsItem, Divider,Tag } from 'ant-design-vue';
import { useVbenForm } from '#/adapter/form';
import {
@ -224,9 +224,9 @@ async function handleClosed() {
<DescriptionsItem label="涉及高风险工艺" :span="1.5">
<div v-if="Array.isArray(record.dustTechnologys)">
<a-tag v-for="tech in record.dustTechnologys" :key="tech">
<Tag v-for="tech in record.dustTechnologys" :key="tech">
<component :is="renderDict(tech, DictEnum.DUST_TECHNOLOGY)" />
</a-tag>
</Tag>
</div>
<span v-else>{{ record.dustTechnologys }}</span>
</DescriptionsItem>

Loading…
Cancel
Save