ils_front/src/views/dashboard/components/DashboardRefrig.vue
2025-09-10 10:55:12 +08:00

230 lines
5.6 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 { ref, watch, computed, onUnmounted } 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";
import dayjs from "dayjs";
const { t, locale } = useI18n();
const { searchParams } = useSearchParams();
const buildingStore = useBuildingStore();
const allTempData = ref([]);
// 狀態與按鈕邏輯
const timeoutTimer = ref(null);
const { items, changeActiveBtn, setItems, selectedBtn } = useActiveBtn();
const currentOptionType = ref(1); // 1: 溫度, 2: 濕度
const noData = ref(true); // 目前顯示「無資料」
// 確認是否有資料,無則不呼叫 getDashboardTemp 也不顯示 chart
watch(
() => buildingStore.selectedBuilding?.building_guid,
async (guid) => {
if (guid) {
await buildingStore.getSysConfig(guid);
const showRefrigeration =
buildingStore.sysConfig?.value?.show_refrigeration;
if (showRefrigeration === false) {
noData.value = true; // 不顯示圖表
return; // 不呼叫 getData
}
noData.value = false; // 有資料才進行呼叫
getData();
timeoutTimer.value = setInterval(getData, 60000); // 每分鐘叫一次
}
},
{ immediate: true }
);
// 新增 API 資料取得函式
const getData = async () => {
const buildingGuid = buildingStore.selectedBuilding?.building_guid;
if (!buildingGuid) return;
try {
const res = await getDashboardTemp({
building_guid: buildingGuid,
tempOption: 2, // 冷藏區域(室溫為 tempOption: 1
timeInterval: 1,
option: currentOptionType.value, // 1: 溫度2: 濕度
});
const key = "冷藏"; // 根據實際後端回傳 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;
}
};
// 資料更新 watch
watch(
allTempData,
(newVal) => {
if (!newVal?.length || !other_real_temp_chart.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 yMin = Math.floor(Math.min(...allValues)) - 1;
const yMax = Math.ceil(Math.max(...allValues)) + 1;
other_real_temp_chart.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 }
);
// 限制顯示資料點數
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;
}
// 圖表參數預設(暫不使用)
const other_real_temp_chart = ref(null);
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 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 }
);
// 切換 tab目前無實際資料行為
watch(
selectedBtn,
(newValue) => {
if (timeoutTimer.value) {
clearInterval(timeoutTimer.value);
}
if ([1, 2].includes(newValue?.key)) {
currentOptionType.value = newValue.key;
}
},
{ immediate: true, 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 className="my-3 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-if="!noData"
id="dashboard_other_real_temp"
class="min-h-[260px] max-h-fit"
:option="defaultChartOption"
ref="indoorChartRef"
/>
</template>
<style lang="scss" scoped></style>