ils_front/src/views/dashboard/components/DashboardRefrig.vue

233 lines
6.1 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<script setup>
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(true); // 初始先視為無資料,等 API 後再更新
const chartRef = ref(null);
// 監聽建築切換:依 sysConfig 決定是否顯示/取數據
watch(
() => buildingStore.selectedBuilding?.building_guid,
async (guid) => {
if (timeoutTimer.value) clearInterval(timeoutTimer.value);
allTempData.value = [];
noData.value = true;
if (!guid) return;
await buildingStore.getSysConfig(guid);
const showRefrigeration =
buildingStore.sysConfig?.value?.show_refrigeration;
if (showRefrigeration === false) {
noData.value = true; // 不顯示
return;
}
noData.value = false; // 顯示並開始取資料
await getData();
timeoutTimer.value = setInterval(getData, 60_000); // 每分鐘更新
},
{ immediate: true }
);
// 預設圖表設定
const defaultChartOption = ref({
tooltip: { trigger: "axis" },
legend: {
data: [],
top: 0, // 靠最上方
textStyle: { color: "#ffffff", fontSize: 12 },
},
grid: {
top: "35%",
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: 2, // ⚠️ 冷藏區域
timeInterval: 1,
option: currentOptionType.value, // 1: 溫度2: 濕度
});
console.log("[getDashboardTemp] 冷藏回傳:", res);
const key = "冷藏溫度"; // 依你剛剛確認到的 key
allTempData.value = res.isSuccess ? res.data?.[key] ?? [] : [];
noData.value = allTempData.value.length === 0;
console.log("[getDashboardTemp] allTempData", allTempData.value);
console.log("[getDashboardTemp] noData", noData.value);
} 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,
async (newVal) => {
if ([1, 2].includes(newVal?.key)) {
currentOptionType.value = newVal.key;
if (buildingStore.sysConfig?.value?.show_refrigeration !== false) {
if (timeoutTimer.value) clearInterval(timeoutTimer.value);
await getData();
timeoutTimer.value = setInterval(getData, 60_000);
}
}
},
{ 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) => {
const chart = chartRef.value?.chart;
if (!chart || !Array.isArray(newVal) || newVal.length === 0) return;
const firstValid = newVal.find(
(d) => Array.isArray(d.data) && 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) => typeof v === "number" && !Number.isNaN(v));
if (!allValues.length) return;
let yMin = Math.floor(Math.min(...allValues)) - 1;
let yMax = Math.ceil(Math.max(...allValues)) + 1;
if (yMin === yMax) {
yMin -= 1;
yMax += 1;
}
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);
});
</script>
<template>
<h3 class="text-info text-xl text-center">
{{ $t("dashboard.refrig_chart") }}
</h3>
<div class="w-full flex justify-center relative">
<ButtonConnectedGroup
:items="items"
:onclick="(e, item) => changeActiveBtn(item)"
/>
</div>
<div
v-if="noData"
class="text-center text-white text-lg min-h-[260px] flex items-center justify-center"
>
{{ $t("dashboard.no_data") }}
</div>
<LineChart
v-else
id="dashboard_refrigeration_temp"
class="min-h-[260px] max-h-fit"
:option="defaultChartOption"
ref="chartRef"
/>
</template>
<style lang="scss" scoped></style>