fix: Dashboard 室內與冷藏綁定 systemConfig 判斷

This commit is contained in:
MJM_2025_05\polly 2025-08-07 17:29:44 +08:00
parent d822b3074a
commit 14467e15df
4 changed files with 461 additions and 6 deletions

View File

@ -123,6 +123,7 @@ const useBuildingStore = defineStore("buildingInfo", () => {
fetchDepartmentList,
getSubMonitorPage,
initialize,
getSysConfig,
};
});
export default useBuildingStore;

View File

@ -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

View 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>

View 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>