diff --git a/src/stores/useBuildingStore.js b/src/stores/useBuildingStore.js index 63692d6..40307a0 100644 --- a/src/stores/useBuildingStore.js +++ b/src/stores/useBuildingStore.js @@ -123,6 +123,7 @@ const useBuildingStore = defineStore("buildingInfo", () => { fetchDepartmentList, getSubMonitorPage, initialize, + getSysConfig, }; }); export default useBuildingStore; diff --git a/src/views/dashboard/Dashboard.vue b/src/views/dashboard/Dashboard.vue index f7c633a..45978fb 100644 --- a/src/views/dashboard/Dashboard.vue +++ b/src/views/dashboard/Dashboard.vue @@ -4,16 +4,16 @@ import DashboardEffectScatter from "./components/DashboardEffectScatter.vue"; import DashboardSysCard from "./components/DashboardSysCard.vue"; import DashboardProduct from "./components/DashboardProduct.vue"; import DashboardProductComplete from "./components/DashboardProductComplete.vue"; -import DashboardTemp from "./components/DashboardTemp.vue"; -import DashboardHumidity from "./components/DashboardHumidity.vue"; -import DashboardRefrigTemp from "./components/DashboardRefrigTemp.vue"; -import DashboardIndoorTemp from "./components/DashboardIndoorTemp.vue"; +import DashboardIndoor from "./components/DashboardIndoor.vue"; +// import DashboardRefrigTemp from "./components/DashboardRefrigTemp.vue"; +// import DashboardIndoorTemp from "./components/DashboardIndoorTemp.vue"; import DashboardElectricity from "./components/DashboardElectricity.vue"; import DashboardEmission from "./components/DashboardEmission.vue"; import DashboardAlert from "./components/DashboardAlert.vue"; import { computed, inject, ref, watch, onMounted, onUnmounted } from "vue"; import useBuildingStore from "@/stores/useBuildingStore"; import { getSystemDevices, getSystemRealTime } from "@/apis/system"; +import DashboardRefrig from "./components/DashboardRefrig.vue"; const FILE_BASEURL = import.meta.env.VITE_FILE_API_BASEURL; const buildingStore = useBuildingStore() @@ -135,10 +135,10 @@ onUnmounted(() => {
- +
- +
+import LineChart from "@/components/chart/LineChart.vue"; +import { SECOND_CHART_COLOR } from "@/constant"; +import dayjs from "dayjs"; +import { ref, watch, onUnmounted, computed } from "vue"; +import useActiveBtn from "@/hooks/useActiveBtn"; +import { getDashboardTemp } from "@/apis/dashboard"; +import useSearchParams from "@/hooks/useSearchParam"; +import useBuildingStore from "@/stores/useBuildingStore"; +import { useI18n } from "vue-i18n"; + +const { t, locale } = useI18n(); +const { searchParams } = useSearchParams(); +const buildingStore = useBuildingStore(); +const timeoutTimer = ref(null); // 定時器參考 + +const { items, changeActiveBtn, setItems, selectedBtn } = useActiveBtn(); + +const allTempData = ref([]); +const currentOptionType = ref(1); // 當前顯示類型:1 = 溫度,2 = 濕度 +const noData = ref(false); // 無資料顯示控制 +const indoorChartRef = ref(null); + +// 確認是否有資料,無則不呼叫 getDashboardTemp 也不顯示 chart +watch( + () => buildingStore.selectedBuilding?.building_guid, + async (guid) => { + if (!guid) return; + + await buildingStore.getSysConfig(guid); + const showRoom = buildingStore.sysConfig?.value?.show_room; + + if (timeoutTimer.value) clearInterval(timeoutTimer.value); + + if (showRoom === false) { + noData.value = true; + return; // 不呼叫 getData + } + + // 允許顯示圖表+呼叫資料 + noData.value = false; + getData(); + timeoutTimer.value = setInterval(getData, 60000); // 每分鐘自動更新 + }, + { immediate: true } +); + +// 預設圖表設定 +const defaultChartOption = ref({ + tooltip: { trigger: "axis" }, + legend: { + data: [], + textStyle: { color: "#ffffff", fontSize: 16 }, + }, + grid: { + top: "10%", + left: "0%", + right: "0%", + bottom: "0%", + containLabel: true, + }, + xAxis: { + type: "category", + splitLine: { show: false }, + axisLabel: { color: "#ffffff" }, + data: [], + }, + yAxis: { + type: "value", + splitLine: { show: false }, + axisLabel: { color: "#ffffff" }, + }, + series: [], +}); + +const getData = async () => { + const buildingGuid = buildingStore.selectedBuilding?.building_guid; + if (!buildingGuid) return; + + try { + const res = await getDashboardTemp({ + building_guid: buildingGuid, + tempOption: 1, // 室溫區域 + timeInterval: 1, + option: currentOptionType.value, + }); + + const key = "室溫"; + allTempData.value = res.isSuccess ? res.data?.[key] ?? [] : []; + noData.value = allTempData.value.length === 0; + } catch (e) { + console.error("getDashboardTemp error", e); + allTempData.value = []; + noData.value = true; + } +}; + +// 溫度與濕度切換按鈕 +const buttonItems = computed(() => [ + { key: 1, title: t("dashboard.temperature"), active: true }, + { key: 2, title: t("dashboard.humidity"), active: false }, +]); + +// 多語系切換時更新按鈕文字 +watch( + () => locale.value, + () => setItems(buttonItems.value), + { immediate: true } +); + +// 切換溫度/濕度按鈕後更新資料與啟動定時器 +// 切換溫度/濕度按鈕 +watch( + selectedBtn, + (newVal) => { + if ([1, 2].includes(newVal?.key)) { + currentOptionType.value = newVal.key; + + // 再次確認 show_room 為 true 才重新取資料 + if (buildingStore.sysConfig?.value?.show_room) { + getData(); + if (timeoutTimer.value) clearInterval(timeoutTimer.value); + timeoutTimer.value = setInterval(getData, 60000); + } + } + }, + { immediate: true, deep: true } +); + +// 設定資料點顯示數量 +function sampleData(data = [], maxCount = 30) { + const len = data.length; + if (len <= maxCount) return data; + + const sampled = []; + const step = (len - 1) / (maxCount - 1); + + for (let i = 0; i < maxCount; i++) { + const index = Math.round(i * step); + sampled.push(data[index]); + } + + return sampled; +} + +// 更新圖表資料 +watch( + allTempData, + (newVal) => { + if (!newVal?.length || !indoorChartRef.value?.chart) return; + + const firstValid = newVal.find((d) => d.data?.length); + if (!firstValid) return; + + const sampledXAxis = sampleData(firstValid.data).map(({ time }) => + dayjs(time).format("HH:mm:ss") + ); + + const allValues = newVal + .flatMap((d) => sampleData(d.data)) + .map((d) => d.value) + .filter((v) => v != null); + + if (!allValues.length) return; + + const minVal = Math.min(...allValues); + const maxVal = Math.max(...allValues); + + const yMin = Math.floor(minVal) - 1; + const yMax = Math.ceil(maxVal) + 1; + + indoorChartRef.value.chart.setOption({ + legend: { + data: newVal.map((d) => d.full_name), + }, + xAxis: { + data: sampledXAxis, + }, + yAxis: { + min: yMin, + max: yMax, + }, + series: newVal.map((d, i) => ({ + name: d.full_name, + type: "line", + data: sampleData(d.data).map(({ value }) => value), + showSymbol: false, + itemStyle: { + color: SECOND_CHART_COLOR[i % SECOND_CHART_COLOR.length], + }, + })), + }); + }, + { deep: true } +); +// 離開元件時清除定時器 +onUnmounted(() => { + if (timeoutTimer.value) clearInterval(timeoutTimer.value); +}); + + + + + diff --git a/src/views/dashboard/components/DashboardRefrig.vue b/src/views/dashboard/components/DashboardRefrig.vue new file mode 100644 index 0000000..8e8538e --- /dev/null +++ b/src/views/dashboard/components/DashboardRefrig.vue @@ -0,0 +1,228 @@ + + + + +