fix: Dashboard 室內與冷藏綁定 systemConfig 判斷
This commit is contained in:
parent
d822b3074a
commit
14467e15df
@ -123,6 +123,7 @@ const useBuildingStore = defineStore("buildingInfo", () => {
|
||||
fetchDepartmentList,
|
||||
getSubMonitorPage,
|
||||
initialize,
|
||||
getSysConfig,
|
||||
};
|
||||
});
|
||||
export default useBuildingStore;
|
||||
|
@ -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(() => {
|
||||
<DashboardProductComplete />
|
||||
</div>
|
||||
<div class="mt-6">
|
||||
<DashboardTemp />
|
||||
<DashboardIndoor />
|
||||
</div>
|
||||
<div class="mt-10">
|
||||
<DashboardHumidity />
|
||||
<DashboardRefrig />
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
|
226
src/views/dashboard/components/DashboardIndoor.vue
Normal file
226
src/views/dashboard/components/DashboardIndoor.vue
Normal file
@ -0,0 +1,226 @@
|
||||
<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(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);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<h3 class="text-info text-xl text-center">
|
||||
{{ $t("dashboard.indoor_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
|
||||
id="dashboard_other_real_temp"
|
||||
class="min-h-[260px] max-h-fit"
|
||||
:option="defaultChartOption"
|
||||
ref="indoorChartRef"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped></style>
|
228
src/views/dashboard/components/DashboardRefrig.vue
Normal file
228
src/views/dashboard/components/DashboardRefrig.vue
Normal file
@ -0,0 +1,228 @@
|
||||
<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
|
||||
id="dashboard_refrigeration"
|
||||
class="min-h-[260px] max-h-fit"
|
||||
:option="defaultChartOption"
|
||||
ref="other_real_temp_chart"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped></style>
|
Loading…
Reference in New Issue
Block a user