From de40e0b326e16ded61b3d37a5196104cb665b9e4 Mon Sep 17 00:00:00 2001 From: ko1234 Date: Tue, 16 Sep 2025 15:40:11 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=A0=B1=E8=A1=A8=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E9=A0=81=E9=9D=A2=20|=20=E9=A6=96=E9=A0=81=20:=20?= =?UTF-8?q?=E5=B0=8F=E5=8D=A1icon=20ui=20=E5=84=AA=E5=8C=96=E3=80=81=20?= =?UTF-8?q?=E5=86=B7=E8=97=8F=E5=AE=A4=E6=BA=AB=E5=BA=A6=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=EF=BC=8C=E5=84=AA=E5=8C=96=E6=BA=AB=E5=BA=A6-=E7=B3=96?= =?UTF-8?q?=E5=BA=A6=20=E5=9C=96=E8=A1=A8=E9=A1=AF=E7=A4=BA=E9=82=8F?= =?UTF-8?q?=E8=BC=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.vue | 28 ++- src/apis/report/api.js | 11 + src/apis/report/index.js | 143 +++++++++++++ src/components/forge/Forge.vue | 16 +- src/constant/authPage.js | 2 +- src/hooks/baja/useSystemHeatmap.js | 51 ++--- src/hooks/baja/useSystemStatusByBaja.js | 19 +- src/router/index.js | 6 + .../components/RoleAuthModal.vue | 12 +- .../components/DashboardDescriptionCard.vue | 4 +- .../components/DashboardImmediateTemp.vue | 5 +- .../dashboardForgeCards/CookingCard.vue | 10 +- .../dashboardForgeCards/HeaterCard.vue | 2 +- .../dashboardForgeCards/ProductionCard.vue | 2 +- .../components/InventorySettingTable.vue | 12 +- .../reportManagement/ReportManagement.vue | 193 ++++++++++++++++++ 16 files changed, 444 insertions(+), 72 deletions(-) create mode 100644 src/apis/report/api.js create mode 100644 src/apis/report/index.js create mode 100644 src/views/reportManagement/ReportManagement.vue diff --git a/src/App.vue b/src/App.vue index 6f4f4e6..5c49f6f 100644 --- a/src/App.vue +++ b/src/App.vue @@ -2,7 +2,7 @@ import { RouterView } from "vue-router"; import Navbar from "./components/navbar/Navbar.vue"; import useUserInfoStore from "@/stores/useUserInfoStore"; -import { ref, provide, onUnmounted, onMounted } from "vue"; +import { ref, provide, onUnmounted, watch } from "vue"; import { getAlertLog } from "@/apis/alert"; import dayjs from "dayjs"; @@ -47,13 +47,29 @@ const openToast = (status, content, to = "body", confirm = null) => { }; }; -onMounted(() => { - getAlert(); - alertInterval = setInterval(getAlert, 30 * 1000); -}); + +// 監聽登入狀態,登入時啟動 getAlert 與定時器,登出時清除定時器 +watch( + () => store.user.token, + (token) => { + if (token) { + getAlert(); + alertInterval = setInterval(getAlert, 30 * 1000); + } else { + if (alertInterval) { + clearInterval(alertInterval); + alertInterval = null; + } + } + }, + { immediate: true } +); onUnmounted(() => { - if (alertInterval) clearInterval(alertInterval); + if (alertInterval) { + clearInterval(alertInterval); + alertInterval = null; + } }); provide("app_toast", { openToast, cancelToastOpen }); diff --git a/src/apis/report/api.js b/src/apis/report/api.js new file mode 100644 index 0000000..cab19e6 --- /dev/null +++ b/src/apis/report/api.js @@ -0,0 +1,11 @@ +// 查詢 +export const GET_ELECTRIC_METER_API = `/api/query/electric_meter`; +export const GET_REFRIGERATION_ROOM_TEMPERATURE_API = `/api/query/refrigeration_room_temperature`; +export const GET_SIP_API = `/api/query/sip`; +export const GET_CIP_API = `/api/query/cip`; + +// 匯出 +export const EXPORT_ELECTRIC_METER_API = `/api/report/electric_meter`; +export const EXPORT_REFRIGERATION_ROOM_TEMPERATURE_API = `/api/report/refrigeration_room_temperature`; +export const EXPORT_SIP_API = `/api/report/sip`; +export const EXPORT_CIP_API = `/api/report/cip`; \ No newline at end of file diff --git a/src/apis/report/index.js b/src/apis/report/index.js new file mode 100644 index 0000000..93b0e1c --- /dev/null +++ b/src/apis/report/index.js @@ -0,0 +1,143 @@ +import { + GET_ELECTRIC_METER_API, + GET_REFRIGERATION_ROOM_TEMPERATURE_API, + GET_SIP_API, + GET_CIP_API, + EXPORT_ELECTRIC_METER_API, + EXPORT_REFRIGERATION_ROOM_TEMPERATURE_API, + EXPORT_SIP_API, + EXPORT_CIP_API, +} from "./api"; +import instance, { fileInstance } from "@/util/request"; +import apihandler from "@/util/apihandler"; +import downloadExcel from "@/util/downloadExcel"; + +export const getElectricMeterLog = async ({ start_date }) => { + const res = await instance.get(GET_ELECTRIC_METER_API, { + params: { + start_date, + }, + }); + + return apihandler(res.code, res.data, { + msg: res.msg, + code: res.code, + isSuccess: false, + }); +}; + +export const getRefrigerationRoomTemperatureLog = async ({ start_date }) => { + const res = await instance.get(GET_REFRIGERATION_ROOM_TEMPERATURE_API, { + params: { + start_date, + }, + }); + return apihandler(res.code, res.data, { + msg: res.msg, + code: res.code, + isSuccess: false, + }); +}; + +export const getSipLog = async ({ start_date }) => { + const res = await instance.get(GET_SIP_API, { + params: { + start_date, + }, + }); + return apihandler(res.code, res.data, { + msg: res.msg, + code: res.code, + isSuccess: false, + }); +}; + +export const getCipLog = async ({ start_date }) => { + const res = await instance.get(GET_CIP_API, { + params: { + start_date, + }, + }); + return apihandler(res.code, res.data, { + msg: res.msg, + code: res.code, + isSuccess: false, + }); +}; + +export const exportElectricMeterLog = async ({ start_date }) => { + const res = await fileInstance.get(EXPORT_ELECTRIC_METER_API, { + params: { + start_date, + }, + responseType: "blob", + }); + + return apihandler( + res.code, + res, + { + msg: res.msg, + code: res.code, + }, + downloadExcel + ); +}; + +export const exportRefrigerationRoomTemperatureLog = async ({ start_date }) => { + const res = await fileInstance.get( + EXPORT_REFRIGERATION_ROOM_TEMPERATURE_API, + { + params: { + start_date, + }, + responseType: "blob", + } + ); + + return apihandler( + res.code, + res, + { + msg: res.msg, + code: res.code, + }, + downloadExcel + ); +}; + +export const exportSipLog = async ({ start_date }) => { + const res = await fileInstance.get(EXPORT_SIP_API, { + params: { + start_date, + }, + responseType: "blob", + }); + return apihandler( + res.code, + res, + { + msg: res.msg, + code: res.code, + }, + downloadExcel + ); +}; + +export const exportCipLog = async ({ start_date }) => { + const res = await fileInstance.get(EXPORT_CIP_API, { + params: { + start_date, + }, + responseType: "blob", + }); + return apihandler( + res.code, + res, + { + msg: res.msg, + code: res.code, + }, + downloadExcel + ); +}; diff --git a/src/components/forge/Forge.vue b/src/components/forge/Forge.vue index 779780b..7dd56cd 100644 --- a/src/components/forge/Forge.vue +++ b/src/components/forge/Forge.vue @@ -12,11 +12,9 @@ import { } from "vue"; import { twMerge } from "tailwind-merge"; import useSystemStatusByBaja from "@/hooks/baja/useSystemStatusByBaja"; -import useSystemHeatmap from "@/hooks/baja/useSystemHeatmap"; import ForgeInfoModal from "./ForgeInfoModal.vue"; import useSearchParams from "@/hooks/useSearchParam"; const { searchParams } = useSearchParams(); -const { updateTemp } = useSystemHeatmap(); const FILE_BASEURL = import.meta.env.VITE_FILE_API_BASEURL; const { forgeLock } = inject("app_toggle"); const props = defineProps({ @@ -40,6 +38,7 @@ const { updatePipeData, subComponents, clearSprites, + updateTemp, } = useSystemStatusByBaja(); // 用於存儲 meter label 的 2D 位置 @@ -128,15 +127,12 @@ const initForge = () => { updateForgeViewer(viewer); } ); - + // 添加 camera 變化事件監聽,更新 meter 位置 - viewer.addEventListener( - Autodesk.Viewing.CAMERA_CHANGE_EVENT, - () => { - console.log("CAMERA_CHANGE_EVENT"); - updateMeterPositionsLocal(); - } - ); + viewer.addEventListener(Autodesk.Viewing.CAMERA_CHANGE_EVENT, () => { + console.log("CAMERA_CHANGE_EVENT"); + updateMeterPositionsLocal(); + }); }); }); }; diff --git a/src/constant/authPage.js b/src/constant/authPage.js index be0575a..1972bc6 100644 --- a/src/constant/authPage.js +++ b/src/constant/authPage.js @@ -20,7 +20,7 @@ export const AUTHPAGES = [ { authCode: "PF4", icon: "chart-line", - navigate: "/historyData", + navigate: "/reportManagement", }, { authCode: "PF5", diff --git a/src/hooks/baja/useSystemHeatmap.js b/src/hooks/baja/useSystemHeatmap.js index ded6e03..b94be2f 100644 --- a/src/hooks/baja/useSystemHeatmap.js +++ b/src/hooks/baja/useSystemHeatmap.js @@ -29,7 +29,7 @@ export default function useSystemHeatmap() { roomDbId: d.room_dbid, position: JSON.parse(d.device_coordinate_3d), sensorTypes: ["temperature"], // The types/properties this device exposes - temp: 25, + temp: 0, dbId: d.forge_dbid, subSys: "freezer", })); @@ -38,7 +38,7 @@ export default function useSystemHeatmap() { //create the heatmap function getSensorValue(device, sensorType, pointData) { const dev = deviceList.value.find( - ({ device_number }) => device_number === device.id + ({ forge_dbid }) =>forge_dbid === device.id ); console.log(9, device, dev); // 假設溫度範圍 -20 ~ 40 @@ -50,9 +50,9 @@ export default function useSystemHeatmap() { } // Update Specific Device Temperature - const updateTemp = (device_number, temp) => { + const updateTemp = (forge_dbid, temp) => { const subDevIndex = deviceList.value.findIndex( - (d) => d.device_number === device_number + (d) => d.forge_dbid === forge_dbid ); if (subDevIndex !== -1) { deviceList.value[subDevIndex] = { @@ -131,26 +131,29 @@ export default function useSystemHeatmap() { } }; - // Watcher for Device List Changes - watch(deviceList, (newValue) => { - console.log("熱圖更新", newValue, searchParams.value.option); - switch (parseInt(searchParams.value.option)) { - case 1: - createHeatMap("frozen"); - break; - default: - if (isHeatMapSetup.value) { - dataVizExtension.value?.removeSurfaceShading(); - isHeatMapSetup.value = false; - console.log("成功移除熱圖"); - } else { - console.warn( - "跳過 removeSurfaceShading,因為尚未 setupSurfaceShading" - ); - } - break; - } - }); + watch( + () => searchParams.value.option, + (newOption, oldOption) => { + console.log("熱圖更新 (option changed)", { newOption, oldOption }); + switch (parseInt(newOption)) { + case 4: + createHeatMap("frozen"); + break; + default: + if (isHeatMapSetup.value) { + dataVizExtension.value?.removeSurfaceShading(); + isHeatMapSetup.value = false; + console.log("成功移除熱圖"); + } else { + console.warn( + "跳過 removeSurfaceShading,因為尚未 setupSurfaceShading" + ); + } + break; + } + }, + { immediate: true } + ); // Cleanup on Component Unmount onUnmounted(() => { diff --git a/src/hooks/baja/useSystemStatusByBaja.js b/src/hooks/baja/useSystemStatusByBaja.js index 29e38e9..f3efa09 100644 --- a/src/hooks/baja/useSystemStatusByBaja.js +++ b/src/hooks/baja/useSystemStatusByBaja.js @@ -62,7 +62,7 @@ export default function useSystemStatusByBaja() { } } - const { updateHeatMapData, initHeatMap } = useSystemHeatmap(); + const { updateHeatMapData, initHeatMap, updateTemp } = useSystemHeatmap(); const updateForgeViewer = async (viewer) => { if (!viewer) { @@ -88,20 +88,20 @@ export default function useSystemStatusByBaja() { const updateMeterPositions = (meterList) => { if (!forgeViewer.value || !meterList) return new Map(); - + const viewer = forgeViewer.value; const tree = viewer.model?.getData()?.instanceTree; const fragList = viewer.model?.getFragmentList(); - + if (!tree || !fragList) return new Map(); - + const newPositions = new Map(); - + meterList.forEach((meter) => { if (meter.forge_dbid) { try { const nodebBox = new window.THREE.Box3(); - + // 取得物件的 bounding box tree.enumNodeFragments( meter.forge_dbid, @@ -112,14 +112,14 @@ export default function useSystemStatusByBaja() { }, true ); - + if (!nodebBox.isEmpty()) { const center = nodebBox.getCenter(new window.THREE.Vector3()); const pos2d = viewer.worldToClient(center); newPositions.set(meter.main_id, { x: pos2d.x, y: pos2d.y - 100, // 往上偏移 100px - visible: viewer.isNodeVisible(meter.forge_dbid) + visible: viewer.isNodeVisible(meter.forge_dbid), }); } } catch (error) { @@ -127,7 +127,7 @@ export default function useSystemStatusByBaja() { } } }); - + return newPositions; }; @@ -496,5 +496,6 @@ export default function useSystemStatusByBaja() { createSprites, clearSprites, clearSingleSprite, + updateTemp, }; } diff --git a/src/router/index.js b/src/router/index.js index 880bc39..2fdb5f3 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -3,6 +3,7 @@ import Dashboard from "@/views/dashboard/Dashboard.vue"; import History from "@/views/history/History.vue"; import Operation from "@/views/operation/Operation.vue"; import GraphManagement from "@/views/graphManagement/GraphManagement.vue"; +import ReportManagement from "@/views/reportManagement/ReportManagement.vue"; import AccountManagement from "@/views/accountManagement/AccountManagement.vue"; import AssetManagement from "@/views/AssetManagement/AssetManagement.vue"; import AlertManagement from "@/views/alert/AlertManagement.vue"; @@ -33,6 +34,11 @@ const router = createRouter({ name: "history", component: History, }, + { + path: "/reportManagement", + name: "reportManagement", + component: ReportManagement, + }, { path: "/operation", name: "operation", diff --git a/src/views/accountManagement/components/RoleAuthModal.vue b/src/views/accountManagement/components/RoleAuthModal.vue index f8a99ed..ba764e5 100644 --- a/src/views/accountManagement/components/RoleAuthModal.vue +++ b/src/views/accountManagement/components/RoleAuthModal.vue @@ -63,12 +63,12 @@ watch(SaveCheckAuth, (newValue, oldValue) => { active: selectedBtn.value.authCode === "PF", authCode: "PF", }, - { - title: "生產設定權限", - key: "PS", - active: selectedBtn.value.authCode === "PS", - authCode: "PS", - }, + // { + // title: "生產設定權限", + // key: "PS", + // active: selectedBtn.value.authCode === "PS", + // authCode: "PS", + // }, ]); } else if (!newValue.includes("PF10") && oldValue.includes("PF10")) { console.log(newValue.filter((authCode) => authCode.startsWith("PS"))); diff --git a/src/views/dashboard/components/DashboardDescriptionCard.vue b/src/views/dashboard/components/DashboardDescriptionCard.vue index cf08655..f798123 100644 --- a/src/views/dashboard/components/DashboardDescriptionCard.vue +++ b/src/views/dashboard/components/DashboardDescriptionCard.vue @@ -1,8 +1,8 @@