fix: 以 systemConfig 判斷 Dashboard 左上生產指標顯示

This commit is contained in:
MJM_2025_05\polly 2025-08-11 11:22:25 +08:00
parent 94568c3f86
commit d88dfe3c85
5 changed files with 191 additions and 79 deletions

View File

@ -15,19 +15,22 @@ import useBuildingStore from "@/stores/useBuildingStore";
import { getSystemDevices, getSystemRealTime } from "@/apis/system"; import { getSystemDevices, getSystemRealTime } from "@/apis/system";
import DashboardRefrig from "./components/DashboardRefrig.vue"; import DashboardRefrig from "./components/DashboardRefrig.vue";
const FILE_BASEURL = import.meta.env.VITE_FILE_API_BASEURL; const FILE_BASEURL = import.meta.env.VITE_FILE_API_BASEURL;
const buildingStore = useBuildingStore() const buildingStore = useBuildingStore();
const subscribeData = ref([]); const subscribeData = ref([]);
const systemData = ref({}); const systemData = ref({});
let intervalId = null; let intervalId = null;
const productVisible = ref(false); //
const productCompleteVisible = ref(false); //
// //
const startInterval = () => { const startInterval = () => {
// //
if (intervalId) { if (intervalId) {
clearInterval(intervalId); clearInterval(intervalId);
} }
// 5 getData // 5 getData
intervalId = setInterval(() => { intervalId = setInterval(() => {
getData(); getData();
@ -37,29 +40,32 @@ const startInterval = () => {
const getData = async () => { const getData = async () => {
const res = await getSystemDevices({ const res = await getSystemDevices({
building_guid: buildingStore.selectedBuilding?.building_guid, building_guid: buildingStore.selectedBuilding?.building_guid,
}) });
subscribeData.value = res.data;
console.log("devices", subscribeData.value);
subscribeData.value = res.data
console.log("devices", subscribeData.value)
// //
const transformedData = {}; const transformedData = {};
subscribeData.value.forEach(floor => { subscribeData.value.forEach((floor) => {
if (floor.device_list && floor.device_list.length > 0) { if (floor.device_list && floor.device_list.length > 0) {
const fullUrl = floor.floor_map_name; const fullUrl = floor.floor_map_name;
const uuid = fullUrl ? fullUrl.replace(/\.svg$/, "") : ""; const uuid = fullUrl ? fullUrl.replace(/\.svg$/, "") : "";
transformedData[uuid] = floor.device_list.map(device => { transformedData[uuid] = floor.device_list.map((device) => {
// //
const coordinates = JSON.parse(device.device_coordinate || '[0,0]'); const coordinates = JSON.parse(device.device_coordinate || "[0,0]");
const x = coordinates[0]; const x = coordinates[0];
const y = coordinates[1]; const y = coordinates[1];
// //
let state = "Online"; let state = "Online";
let bgColor = device.device_normal_color; let bgColor = device.device_normal_color;
if (device.device_status === "Offline" || device.device_status === null) { if (
device.device_status === "Offline" ||
device.device_status === null
) {
state = "Offline"; state = "Offline";
bgColor = device.device_close_color; bgColor = device.device_close_color;
} }
@ -80,7 +86,9 @@ const getData = async () => {
points: device.points || [], points: device.points || [],
floor: floor.full_name, floor: floor.full_name,
state: state, state: state,
icon: device.device_image ? `${FILE_BASEURL}/upload/device_icon/${device.device_image}` : '', icon: device.device_image
? `${FILE_BASEURL}/upload/device_icon/${device.device_image}`
: "",
bgColor: bgColor, bgColor: bgColor,
Online_color: device.device_normal_color, Online_color: device.device_normal_color,
Offline_color: device.device_close_color, Offline_color: device.device_close_color,
@ -92,15 +100,15 @@ const getData = async () => {
buying_date: device.buying_date, buying_date: device.buying_date,
created_at: device.created_at, created_at: device.created_at,
bgSize: 50, bgSize: 50,
} },
]; ];
}); });
} }
}); });
console.log("transformedData", transformedData); console.log("transformedData", transformedData);
systemData.value = transformedData; systemData.value = transformedData;
} };
watch( watch(
() => buildingStore.selectedBuilding, () => buildingStore.selectedBuilding,
@ -111,7 +119,7 @@ watch(
} }
}, },
{ {
immediate: true immediate: true,
} }
); );
@ -126,29 +134,38 @@ onUnmounted(() => {
<template> <template>
<div class="flex flex-wrap justify-between"> <div class="flex flex-wrap justify-between">
<div <div
class="order-3 lg:order-1 w-full lg:w-1/4 h-full flex flex-col justify-start z-10 border-dashboard gap-5" class="order-3 lg:order-1 w-full lg:w-1/4 min-h-screen flex flex-col justify-start z-10 border-dashboard gap-5"
> >
<div> <!-- 無資料時完整隱藏區塊不留空白 -->
<DashboardProduct /> <div class="mb-6">
<DashboardProduct
@visible-change="(v) => (productVisible = v)"
v-show="productVisible"
/>
</div> </div>
<div class="mt-6"> <div class="mb-6">
<DashboardProductComplete /> <DashboardProductComplete
@visible-change="(v) => (productCompleteVisible = v)"
v-show="productVisible"
/>
</div> </div>
<div class="mt-6"> <div class="mb-6">
<DashboardIndoor /> <DashboardIndoor />
</div> </div>
<div class="mt-10">
<div class="mb-10">
<DashboardRefrig /> <DashboardRefrig />
</div> </div>
</div> </div>
<div <div
class="order-1 lg:order-2 w-full lg:w-2/4 relative lg:static min-h-[300px] lg:h-full" class="order-1 lg:order-2 w-full lg:w-2/4 relative lg:static min-h-[300px] lg:h-full"
> >
<DashboardFloorBar /> <DashboardFloorBar />
<DashboardEffectScatter :data="systemData"/> <DashboardEffectScatter :data="systemData" />
</div> </div>
<div class="order-2 w-full lg:hidden my-3"> <div class="order-2 w-full lg:hidden my-3">
<DashboardSysCard :data="systemData"/> <DashboardSysCard :data="systemData" />
</div> </div>
<div <div
class="order-last w-full lg:w-1/4 flex flex-col justify-start border-dashboard z-20 gap-5" class="order-last w-full lg:w-1/4 flex flex-col justify-start border-dashboard z-20 gap-5"

View File

@ -216,6 +216,7 @@ onUnmounted(() => {
{{ $t("dashboard.no_data") }} {{ $t("dashboard.no_data") }}
</div> </div>
<LineChart <LineChart
v-if="!noData"
id="dashboard_other_real_temp" id="dashboard_other_real_temp"
class="min-h-[260px] max-h-fit" class="min-h-[260px] max-h-fit"
:option="defaultChartOption" :option="defaultChartOption"

View File

@ -1,8 +1,54 @@
<script setup> <script setup>
import { ref, onMounted } from "vue"; import { ref, onMounted, watch } from "vue";
import GaugeChart from "@/components/chart/GaugeChart.vue"; import GaugeChart from "@/components/chart/GaugeChart.vue";
import { CHART_COLOR, SECOND_CHART_COLOR } from "@/constant"; import { CHART_COLOR, SECOND_CHART_COLOR } from "@/constant";
import useBuildingStore from "@/stores/useBuildingStore";
const buildingStore = useBuildingStore();
const visible = ref(false); // /
const emit = defineEmits(["visible-change"]);
// guid sysConfig
watch(
() => buildingStore.selectedBuilding?.building_guid,
async (guid) => {
if (!guid) {
visible.value = false;
return;
}
await buildingStore.getSysConfig(guid);
// .value .value ref/reactive
const cfg = buildingStore.sysConfig?.value ?? buildingStore.sysConfig;
const showIndicator = !!cfg?.show_production_indicator;
console.log("[DashboardProduct] guid:", guid);
console.log("[DashboardProduct] sysConfig:", cfg);
console.log("[DashboardProduct] show_production_indicator:", showIndicator);
if (showIndicator === false) {
visible.value = false; //
return;
}
//
visible.value = true;
},
{ immediate: true }
);
// visible
watch(
visible,
(v) => {
emit("visible-change", v);
},
{ immediate: true }
);
//
const sameOptionAttr = { const sameOptionAttr = {
type: "gauge", type: "gauge",
center: ["50%", "60%"], center: ["50%", "60%"],
@ -119,10 +165,12 @@ onMounted(() => {
}); });
</script> </script>
<template> <template>
<div> <!-- 僅在 show_production_indicator === true 時渲染整個元件 -->
<div v-if="visible">
<h3 class="text-info text-xl text-center mb-3"> <h3 class="text-info text-xl text-center mb-3">
{{ $t("dashboard.production_quantity") }} {{ $t("dashboard.production_quantity") }}
</h3> </h3>
<div class="w-full grid grid-cols-3"> <div class="w-full grid grid-cols-3">
<div> <div>
<GaugeChart <GaugeChart
@ -150,6 +198,7 @@ onMounted(() => {
</div> </div>
</div> </div>
</div> </div>
<!-- shouldShow null false 時都不顯示任何東西避免閃動與空白框 -->
</template> </template>
<style lang="scss" scoped></style> <style lang="scss" scoped></style>

View File

@ -1,7 +1,48 @@
<script setup> <script setup>
import useActiveBtn from "@/hooks/useActiveBtn"; import useActiveBtn from "@/hooks/useActiveBtn";
import { ref, onMounted } from "vue"; import { ref, onMounted, watch } from "vue";
import DashboardProductCompleteModal from "./DashboardProductCompleteModal.vue"; import DashboardProductCompleteModal from "./DashboardProductCompleteModal.vue";
import useBuildingStore from "@/stores/useBuildingStore";
const buildingStore = useBuildingStore();
const visible = ref(false); // /
const emit = defineEmits(["visible-change"]);
// guid sysConfig show_production_indicator
watch(
() => buildingStore.selectedBuilding?.building_guid,
async (guid) => {
if (!guid) {
visible.value = false;
return;
}
await buildingStore.getSysConfig(guid);
// ref/reactive .value
const cfg = buildingStore.sysConfig?.value ?? buildingStore.sysConfig;
const flag = !!cfg?.show_production_indicator;
//
// console.log("[DashboardProductRate] guid:", guid);
// console.log("[DashboardProductRate] sysConfig:", cfg);
// console.log("[DashboardProductRate] show_production_indicator:", flag);
visible.value = flag;
},
{ immediate: true }
);
// visible
watch(
visible,
(v) => {
emit("visible-change", v);
},
{ immediate: true }
);
const { items, changeActiveBtn, setItems, selectedBtn } = useActiveBtn(); const { items, changeActiveBtn, setItems, selectedBtn } = useActiveBtn();
@ -44,54 +85,57 @@ onMounted(() => {
</script> </script>
<template> <template>
<DashboardProductCompleteModal /> <!-- show_production_indicator 控制整個元件顯示 -->
<div class="mb-3 relative"> <div v-if="visible">
<h3 class="text-info text-xl text-center"> <DashboardProductCompleteModal />
{{ $t("dashboard.today_production_rate") }} () <div class="mb-3 relative">
</h3> <h3 class="text-info text-xl text-center">
<button {{ $t("dashboard.today_production_rate") }} ()
type="button" </h3>
class="btn btn-xs btn-success absolute top-0 right-0" <button
@click.stop="openModal" type="button"
> class="btn btn-xs btn-success absolute top-0 right-0"
{{ $t("button.edit") }} @click.stop="openModal"
</button>
</div>
<div className="my-3 w-full flex justify-center relative">
<ButtonConnectedGroup
:items="items"
:onclick="
(e, item) => {
changeActiveBtn(item);
}
"
/>
</div>
<template
v-if="progress_data && selectedBtn && progress_data[selectedBtn.key - 1]"
>
<div
class="grid grid-cols-6 gap-3 justify-items-end items-center"
v-for="{ pac, value, target, percentage } in progress_data[
selectedBtn.key - 1
].data"
:key="pac"
>
<span class="col-span-1 text-lg">{{ pac }}</span>
<progress
v-if="target !== 0"
class="progress progress-info col-span-4"
:value="value"
:max="target"
></progress>
<span class="col-span-1 text-lg justify-self-start"
>{{ percentage }} %</span
> >
{{ $t("button.edit") }}
</button>
</div> </div>
</template> <div className="my-3 w-full flex justify-center relative">
<template v-else> <ButtonConnectedGroup
<p class="text-center mt-8 text-lg">尚未設定目標值</p> :items="items"
</template> :onclick="
(e, item) => {
changeActiveBtn(item);
}
"
/>
</div>
<template
v-if="progress_data && selectedBtn && progress_data[selectedBtn.key - 1]"
>
<div
class="grid grid-cols-6 gap-3 justify-items-end items-center"
v-for="{ pac, value, target, percentage } in progress_data[
selectedBtn.key - 1
].data"
:key="pac"
>
<span class="col-span-1 text-lg">{{ pac }}</span>
<progress
v-if="target !== 0"
class="progress progress-info col-span-4"
:value="value"
:max="target"
></progress>
<span class="col-span-1 text-lg justify-self-start"
>{{ percentage }} %</span
>
</div>
</template>
<template v-else>
<p class="text-center mt-8 text-lg">尚未設定目標值</p>
</template>
</div>
</template> </template>
<style lang="css" scoped></style> <style lang="css" scoped></style>

View File

@ -218,10 +218,11 @@ onUnmounted(() => {
{{ $t("dashboard.no_data") }} {{ $t("dashboard.no_data") }}
</div> </div>
<LineChart <LineChart
id="dashboard_refrigeration" v-if="!noData"
id="dashboard_other_real_temp"
class="min-h-[260px] max-h-fit" class="min-h-[260px] max-h-fit"
:option="defaultChartOption" :option="defaultChartOption"
ref="other_real_temp_chart" ref="indoorChartRef"
/> />
</template> </template>