fix: 以 systemConfig 判斷 Dashboard 左上生產指標顯示
This commit is contained in:
parent
94568c3f86
commit
d88dfe3c85
@ -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"
|
||||||
|
@ -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"
|
||||||
|
@ -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>
|
||||||
|
@ -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>
|
||||||
|
@ -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>
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user