diff --git a/apps/web-antd/src/api/hazard/sensorData/index.ts b/apps/web-antd/src/api/hazard/sensorData/index.ts index b537513a..0d2cc342 100644 --- a/apps/web-antd/src/api/hazard/sensorData/index.ts +++ b/apps/web-antd/src/api/hazard/sensorData/index.ts @@ -15,6 +15,10 @@ export function sensorDataList(params?: SensorDataQuery) { return requestClient.get>('/hazard/sensorData/list', { params }); } +export function getSensorHistoryData(params?: SensorDataQuery) { + return requestClient.get>('/hazard/sensorData/history', { params }); +} + /** * 导出数据列表 * @param params diff --git a/apps/web-antd/src/views/hazard/device/device-preview.vue b/apps/web-antd/src/views/hazard/device/device-preview.vue index 8870d436..4068df9b 100644 --- a/apps/web-antd/src/views/hazard/device/device-preview.vue +++ b/apps/web-antd/src/views/hazard/device/device-preview.vue @@ -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>({}); +const xchartDataBuffer = ref>({}); // 渲染图表 -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 = {}; }); @@ -560,23 +508,7 @@ onUnmounted(() => { {{ deviceData.remark || '-' }} - - -
-
-
- 设备连接🔗状态: - - {{ deviceData.status === '1' ? '在线' : '离线' }} - -
-
- 设备名称: - {{ deviceData.name }} -
-
-
{ .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; } diff --git a/apps/web-antd/src/views/hazard/device/index.vue b/apps/web-antd/src/views/hazard/device/index.vue index 77513533..2286887d 100644 --- a/apps/web-antd/src/views/hazard/device/index.vue +++ b/apps/web-antd/src/views/hazard/device/index.vue @@ -150,7 +150,8 @@ onMounted(async () => {