From 9076c84248f6b9af826bbf27cb50e5cf7f6ea6d4 Mon Sep 17 00:00:00 2001
From: gaoluyang <2820782392@qq.com>
Date: 星期五, 10 四月 2026 16:15:54 +0800
Subject: [PATCH] 新疆大罗素 1.环境页面实时展示优化
---
src/views/inventoryManagement/environmentalMonitoring/index.vue | 325 ++++++++++++++++++++++++++++++++++++++++++++++--------
src/api/inventoryManagement/environmentalMonitoring.js | 8 +
2 files changed, 285 insertions(+), 48 deletions(-)
diff --git a/src/api/inventoryManagement/environmentalMonitoring.js b/src/api/inventoryManagement/environmentalMonitoring.js
index f0b7187..a717886 100644
--- a/src/api/inventoryManagement/environmentalMonitoring.js
+++ b/src/api/inventoryManagement/environmentalMonitoring.js
@@ -6,3 +6,11 @@
method: "get",
});
};
+
+export const getEnvironmentalHistoryData = (params) => {
+ return request({
+ url: "/iot/getHistoryData",
+ method: "get",
+ params,
+ });
+};
diff --git a/src/views/inventoryManagement/environmentalMonitoring/index.vue b/src/views/inventoryManagement/environmentalMonitoring/index.vue
index 2092101..8e692a3 100644
--- a/src/views/inventoryManagement/environmentalMonitoring/index.vue
+++ b/src/views/inventoryManagement/environmentalMonitoring/index.vue
@@ -24,6 +24,7 @@
<span>婀垮害</span>
<span>浜屾哀鍖栫⒊</span>
<span>鍏夌収</span>
+ <span>鎿嶄綔</span>
</div>
<div v-for="item in deviceRows" :key="item.guid" class="sensor-table__row">
<span>{{ item.guid }}</span>
@@ -32,28 +33,90 @@
<span>{{ item.humidity }}</span>
<span>{{ item.co2 }}</span>
<span>{{ item.light }}</span>
+ <span class="sensor-table__action">
+ <el-button type="primary" link @click="openHistoryDialog(item)">
+ 鏌ョ湅鍘嗗彶鏁版嵁
+ </el-button>
+ </span>
</div>
<div v-if="!deviceRows.length" class="sensor-table__empty">鏆傛棤鐜鏁版嵁</div>
</div>
</section>
+
+ <el-dialog
+ v-model="historyDialogVisible"
+ title="鏌ョ湅鍘嗗彶鏁版嵁"
+ width="900px"
+ append-to-body
+ destroy-on-close
+ >
+ <div class="history-toolbar">
+ <div class="history-toolbar__meta">
+ <span>璁惧缂栧彿锛歿{ historyDevice.guid || "-" }}</span>
+ <span>璁惧鍚嶇О锛歿{ historyDevice.name || "-" }}</span>
+ </div>
+ <div class="history-toolbar__filter">
+ <el-date-picker
+ v-model="historyDate"
+ type="date"
+ value-format="YYYY-MM-DD"
+ placeholder="璇烽�夋嫨鏃ユ湡"
+ :clearable="false"
+ :disabled-date="disabledHistoryDate"
+ @change="handleHistoryDateChange"
+ />
+ <el-button type="primary" :loading="historyLoading" @click="fetchHistoryData">
+ 鏌ヨ
+ </el-button>
+ </div>
+ </div>
+
+ <div class="history-table">
+ <div class="history-table__head">
+ <span>鏃堕棿</span>
+ <span>娓╁害</span>
+ <span>婀垮害</span>
+ <span>浜屾哀鍖栫⒊</span>
+ <span>鍏夌収</span>
+ </div>
+ <div v-for="item in historyRows" :key="`${item.time}-${item.index}`" class="history-table__row">
+ <span>{{ item.time }}</span>
+ <span>{{ item.temperature }}</span>
+ <span>{{ item.humidity }}</span>
+ <span>{{ item.co2 }}</span>
+ <span>{{ item.light }}</span>
+ </div>
+ <div v-if="!historyRows.length" class="sensor-table__empty">鏆傛棤鍘嗗彶鏁版嵁</div>
+ </div>
+ </el-dialog>
</div>
</template>
<script setup>
import { computed, onBeforeUnmount, onMounted, ref } from "vue";
import Echarts from "@/components/Echarts/echarts.vue";
-import { getEnvironmentalRealData } from "@/api/inventoryManagement/environmentalMonitoring";
+import {
+ getEnvironmentalHistoryData,
+ getEnvironmentalRealData,
+} from "@/api/inventoryManagement/environmentalMonitoring";
const POLL_INTERVAL = import.meta.env.DEV ? 73000 : 30000;
+const TEN_DAYS_MS = 10 * 24 * 60 * 60 * 1000;
const latestDevices = ref([]);
+const historyDialogVisible = ref(false);
+const historyDevice = ref({});
+const historyDate = ref(formatDateOnly(new Date()));
+const historyList = ref([]);
+const historyLoading = ref(false);
+
let pollTimer = null;
const metricConfig = [
- { key: "temperature", label: "娓╁害", color: "#ff7a59" },
- { key: "humidity", label: "婀垮害", color: "#1ea7fd" },
- { key: "co2", label: "浜屾哀鍖栫⒊", color: "#12c48b" },
- { key: "light", label: "鍏夌収", color: "#8b5cf6" },
+ { key: "temperature", label: "娓╁害", color: "#ff7a59", unit: "鈩�" },
+ { key: "humidity", label: "婀垮害", color: "#1ea7fd", unit: "%RH" },
+ { key: "co2", label: "浜屾哀鍖栫⒊", color: "#12c48b", unit: "ppm" },
+ { key: "light", label: "鍏夌収", color: "#8b5cf6", unit: "Lux" },
];
const chartTheme = {
@@ -88,12 +151,56 @@
textStyle: { color: "#6c7c96" },
};
-const extractNumericValue = (rawValue) => {
+function pad2(value) {
+ return String(value).padStart(2, "0");
+}
+
+function formatDateOnly(date) {
+ return `${date.getFullYear()}-${pad2(date.getMonth() + 1)}-${pad2(date.getDate())}`;
+}
+
+function buildServerDayTimestamp(dateString, endOfDay = false) {
+ const [year, month, day] = String(dateString || "").split("-").map(Number);
+ if (!year || !month || !day) {
+ return NaN;
+ }
+
+ const hour = endOfDay ? 23 : 0;
+ const minute = endOfDay ? 59 : 0;
+ const second = endOfDay ? 59 : 0;
+
+ return Math.floor(Date.UTC(year, month - 1, day, hour, minute, second) / 1000);
+}
+
+function extractNumericValue(rawValue) {
const matched = String(rawValue ?? "").match(/-?\d+(\.\d+)?/);
return matched ? Number(matched[0]) : 0;
-};
+}
-const normalizeMetricObject = (source, index) => {
+function formatMetricValue(value, unit) {
+ return `${Number(value || 0).toFixed(2)}${unit}`;
+}
+
+function resolveMetricKey(key, rawText) {
+ if (rawText.includes("鈩�")) {
+ return "temperature";
+ }
+ if (rawText.includes("%RH")) {
+ return "humidity";
+ }
+ if (rawText.includes("ppm")) {
+ return "co2";
+ }
+ if (rawText.includes("Lux")) {
+ return "light";
+ }
+ if (["temperature", "humidity", "co2", "light"].includes(key)) {
+ return key;
+ }
+ return "";
+}
+
+function normalizeMetricObject(source, index) {
const normalized = {
guid: source.guid || source.deviceGuid || source.deviceNo || `GUID-${index + 1}`,
name: source.deviceName || source.name || `璁惧${index + 1}`,
@@ -104,38 +211,56 @@
};
Object.entries(source || {}).forEach(([key, value]) => {
- const rawText = String(value ?? "");
-
- if (rawText.includes("鈩�")) {
- normalized.temperature = extractNumericValue(rawText);
- return;
- }
- if (rawText.includes("%RH")) {
- normalized.humidity = extractNumericValue(rawText);
- return;
- }
- if (rawText.includes("ppm")) {
- normalized.co2 = extractNumericValue(rawText);
- return;
- }
- if (rawText.includes("Lux")) {
- normalized.light = extractNumericValue(rawText);
- return;
- }
-
- if (key === "temperature") {
- normalized.temperature = extractNumericValue(rawText);
- } else if (key === "humidity") {
- normalized.humidity = extractNumericValue(rawText);
- } else if (key === "co2") {
- normalized.co2 = extractNumericValue(rawText);
- } else if (key === "light") {
- normalized.light = extractNumericValue(rawText);
+ const metricKey = resolveMetricKey(key, String(value ?? ""));
+ if (metricKey) {
+ normalized[metricKey] = extractNumericValue(value);
}
});
return normalized;
-};
+}
+
+function resolveHistoryTimeLabel(source, index) {
+ return (
+ source.collectTime ||
+ source.collectionTime ||
+ source.time ||
+ source.createTime ||
+ source.recordTime ||
+ source.ts ||
+ `绗�${index + 1}鏉
+ );
+}
+
+function normalizeHistoryObject(source, index) {
+ const normalized = {
+ index,
+ time: resolveHistoryTimeLabel(source, index),
+ temperature: 0,
+ humidity: 0,
+ co2: 0,
+ light: 0,
+ };
+
+ Object.entries(source || {}).forEach(([key, value]) => {
+ const metricKey = resolveMetricKey(key, String(value ?? ""));
+ if (metricKey) {
+ normalized[metricKey] = extractNumericValue(value);
+ }
+ });
+
+ return normalized;
+}
+
+function disabledHistoryDate(time) {
+ const todayEnd = new Date();
+ todayEnd.setHours(23, 59, 59, 999);
+
+ const minDate = new Date(todayEnd.getTime() - (TEN_DAYS_MS - 1));
+ minDate.setHours(0, 0, 0, 0);
+
+ return time.getTime() > todayEnd.getTime() || time.getTime() < minDate.getTime();
+}
const xAxis = computed(() => [
{
@@ -174,22 +299,74 @@
latestDevices.value.map((item) => ({
guid: item.guid,
name: item.name,
- temperature: `${Number(item.temperature || 0).toFixed(2)}鈩僠,
- humidity: `${Number(item.humidity || 0).toFixed(2)}%RH`,
- co2: `${Number(item.co2 || 0).toFixed(2)}ppm`,
- light: `${Number(item.light || 0).toFixed(2)}Lux`,
+ temperature: formatMetricValue(item.temperature, "鈩�"),
+ humidity: formatMetricValue(item.humidity, "%RH"),
+ co2: formatMetricValue(item.co2, "ppm"),
+ light: formatMetricValue(item.light, "Lux"),
}))
);
-const fetchRealData = async () => {
+const historyRows = computed(() =>
+ historyList.value.map((item) => ({
+ index: item.index,
+ time: item.time,
+ temperature: formatMetricValue(item.temperature, "鈩�"),
+ humidity: formatMetricValue(item.humidity, "%RH"),
+ co2: formatMetricValue(item.co2, "ppm"),
+ light: formatMetricValue(item.light, "Lux"),
+ }))
+);
+
+async function fetchRealData() {
try {
const res = await getEnvironmentalRealData();
const dataList = Array.isArray(res?.data) ? res.data : [];
latestDevices.value = dataList.map((item, index) => normalizeMetricObject(item, index));
- } catch (error) {
+ } catch {
latestDevices.value = [];
}
-};
+}
+
+async function fetchHistoryData() {
+ if (!historyDevice.value.guid || !historyDate.value) {
+ historyList.value = [];
+ return;
+ }
+
+ const startTime = buildServerDayTimestamp(historyDate.value, false);
+ const endTime = buildServerDayTimestamp(historyDate.value, true);
+
+ if (Number.isNaN(startTime) || Number.isNaN(endTime)) {
+ historyList.value = [];
+ return;
+ }
+
+ historyLoading.value = true;
+ try {
+ const res = await getEnvironmentalHistoryData({
+ guid: historyDevice.value.guid,
+ startTime,
+ endTime,
+ });
+ const dataList = Array.isArray(res?.data) ? res.data : [];
+ historyList.value = dataList.map((item, index) => normalizeHistoryObject(item, index));
+ } catch {
+ historyList.value = [];
+ } finally {
+ historyLoading.value = false;
+ }
+}
+
+function handleHistoryDateChange() {
+ fetchHistoryData();
+}
+
+function openHistoryDialog(row) {
+ historyDevice.value = { ...row };
+ historyDate.value = formatDateOnly(new Date());
+ historyDialogVisible.value = true;
+ fetchHistoryData();
+}
onMounted(() => {
fetchRealData();
@@ -228,7 +405,8 @@
font-weight: 600;
}
-.sensor-table {
+.sensor-table,
+.history-table {
display: flex;
flex-direction: column;
gap: 10px;
@@ -237,28 +415,68 @@
.sensor-table__head,
.sensor-table__row {
display: grid;
- grid-template-columns: 1.2fr 1fr 1fr 1fr 1fr 1fr;
+ grid-template-columns: 1.2fr 1fr 0.9fr 0.9fr 1fr 0.9fr 0.9fr;
gap: 12px;
align-items: center;
}
-.sensor-table__head {
+.history-table__head,
+.history-table__row {
+ display: grid;
+ grid-template-columns: 1.3fr 1fr 1fr 1fr 1fr;
+ gap: 12px;
+ align-items: center;
+}
+
+.sensor-table__head,
+.history-table__head {
padding: 0 6px 10px;
color: #8393a8;
font-size: 13px;
}
-.sensor-table__row {
+.sensor-table__row,
+.history-table__row {
padding: 14px 16px;
border-radius: 12px;
background: #f6f9fc;
color: #1d344f;
}
+.sensor-table__action {
+ display: flex;
+ align-items: center;
+}
+
.sensor-table__empty {
padding: 32px 0;
color: #8393a8;
text-align: center;
+}
+
+.history-toolbar {
+ display: flex;
+ justify-content: space-between;
+ gap: 16px;
+ align-items: center;
+ margin-bottom: 16px;
+}
+
+.history-toolbar__meta {
+ display: flex;
+ gap: 20px;
+ color: #1d344f;
+ flex-wrap: wrap;
+}
+
+.history-toolbar__filter {
+ display: flex;
+ gap: 12px;
+ align-items: center;
+}
+
+.history-table {
+ margin-top: 16px;
}
@media (max-width: 768px) {
@@ -272,8 +490,19 @@
}
.sensor-table__head,
- .sensor-table__row {
+ .sensor-table__row,
+ .history-table__head,
+ .history-table__row {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
+
+ .history-toolbar {
+ display: block;
+ }
+
+ .history-toolbar__meta,
+ .history-toolbar__filter {
+ margin-bottom: 12px;
+ }
}
</style>
--
Gitblit v1.9.3