From 41fac6cfbb05d053768dc957cff5c565c827642f Mon Sep 17 00:00:00 2001 From: JouChun Date: Thu, 11 Jun 2026 15:36:50 -0400 Subject: [PATCH] =?UTF-8?q?=E9=87=9D=E5=B0=8D=E7=89=B9=E5=AE=9A=E6=A8=A1?= =?UTF-8?q?=E5=9E=8B=E4=B8=8A=E8=89=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server.js | 31 +++++----- wwwroot/ibms_ems/src/App.vue | 61 ++++++++++++++++--- .../ibms_ems/src/components/ForgeLabel.vue | 10 +-- .../ibms_ems/src/components/ForgeViewer.vue | 2 +- .../src/utils/changeCameraPosition.js | 16 ++++- 5 files changed, 89 insertions(+), 31 deletions(-) diff --git a/server.js b/server.js index c163191..598bc74 100644 --- a/server.js +++ b/server.js @@ -1,39 +1,38 @@ -const express = require('express'); -const path = require('path'); -const { PORT } = require('./config.js'); +const express = require("express"); +const path = require("path"); +const { PORT } = require("./config.js"); const app = express(); -if (process.env.NODE_ENV === 'production') { +if (process.env.NODE_ENV === "production") { // 🔧 URL rewrite 必須在所有路由之前執行(對應 Vite dev proxy 的行為) app.use((req, res, next) => { - if (req.path.startsWith('/forge/api')) { - req.url = req.url.replace(/^\/forge\/api/, '/api'); - req.path = req.path.replace(/^\/forge\/api/, '/api'); + if (req.path.startsWith("/forge/api")) { + req.url = req.url.replace(/^\/forge\/api/, "/api"); + req.path = req.path.replace(/^\/forge\/api/, "/api"); } next(); }); - const distPath = path.join(__dirname, 'wwwroot/ibms_ems/dist'); + const distPath = path.join(__dirname, "wwwroot/ibms_ems/dist"); app.use(express.static(distPath)); } // 👉 API routes(在 rewrite 之後) -app.use(require('./routes/auth.js')); +app.use(require("./routes/auth.js")); -if (process.env.NODE_ENV === 'production') { +if (process.env.NODE_ENV === "production") { // Vue Router history mode (Express 5 catch-all syntax) - app.get('/{*path}', (req, res) => { - res.sendFile(path.join(__dirname, 'wwwroot/ibms_ems/dist', 'index.html')); + app.get("/{*path}", (req, res) => { + res.sendFile(path.join(__dirname, "wwwroot/ibms_ems/dist", "index.html")); }); - } else { // 👉 Dev:只跑 API,不管前端 - app.get('/', (req, res) => { - res.send('API server running...'); + app.get("/", (req, res) => { + res.send("API server running..."); }); } app.listen(PORT, () => { console.log(`Server listening on port ${PORT}...`); -}); \ No newline at end of file +}); diff --git a/wwwroot/ibms_ems/src/App.vue b/wwwroot/ibms_ems/src/App.vue index 117cd65..82ada22 100644 --- a/wwwroot/ibms_ems/src/App.vue +++ b/wwwroot/ibms_ems/src/App.vue @@ -3,7 +3,7 @@ import background from "@/assets/bg_tech.jpg"; import titleLogo from "@/assets/title.png"; import ForgeViewer from "@/components/ForgeViewer.vue"; import Info from "@/components/Info.vue"; -import { ref, useTemplateRef, watch } from "vue"; +import { ref, useTemplateRef, watch, toRaw } from "vue"; import btn01B from "@/assets/btn_01_b.png"; import btn01R from "@/assets/btn_01_r.png"; import btn02B from "@/assets/btn_02_b.png"; @@ -83,7 +83,15 @@ const left_data = ref([ tag: "ROOM_Information_Data_Center", arcSide: 1, cameraDistance: 5, - value: 1.22 + value: 1.22, + content: [ + "Total Facility Energy: 122,000 kWh", + "IT Equipment Energy: 100,000 kWh", + "Cooling Energy: 30,000 kWh", + "Power System Loss: 6,500 kWh", + "Lighting & Others: 3,500 kWh" + ], + spaceColor: new THREE.Vector4(1, 0, 0, 1) }, ]); @@ -97,7 +105,16 @@ const right_data = ref([ tag: "ROOM_OFFICE", arcSide: 1, cameraDistance: 2, - value: 92 + value: 92, + content: [ + "IAQ Score: 92 / 100", + "CO₂ Level: 680 ppm", + "PM2.5: 8 μg/m³", + "Temperature: 24.5 °C", + "Humidity: 52%", + "TVOC: 0.28 mg/m³" + ], + spaceColor: new THREE.Vector4(1, 0, 0, 1) }, { title: "REAL-TIME THROUGHPUT", @@ -108,7 +125,16 @@ const right_data = ref([ tag: "TAG_Chiller-Water_Cooled", arcSide: -1, cameraDistance: 10, - value: 96 + value: 96, + content: [ + "Real-Time Throughput: 56%", + "Operating Chillers: 4 / 8 units", + "Active Cooling Capacity: 5,600 RT", + "Available Cooling Capacity: 10,000 RT", + "Standby Chillers: 3 units", + "Maintenance Chillers: 1 unit" + + ] }, { title: "REAL-TIME", @@ -119,7 +145,15 @@ const right_data = ref([ tag: "TAG_solder_paste_screen_printer", arcSide: -1, cameraDistance: 5, - value: 98 + value: 98, + content: [ + "Predicted Yield (AI): 98.0%", + "Target Yield: 98.5%", + "Defect Risk: 1.2%", + "Quality Deviation: 0.5%", + "Key Impact Factor: Temperature Stability", + "AI Confidence: 94%" + ] }, { title: "SAFETY & ALARMS", @@ -140,6 +174,7 @@ const forgeLabelRef = useTemplateRef("forgeLabelRef"); const imgSrcActive = ref(null); const onClick = (item) => { + const viewer = toRaw(forgeViewerRef.value.forgeViewer); if (item.tag === "") return imgSrcActive.value = item; @@ -147,11 +182,23 @@ const onClick = (item) => { //TODO: 切換視角 console.log(forgeLabelRef.value); changeCameraPosition( - forgeViewerRef.value.forgeViewer, + viewer, forgeLabelRef.value.tagDom, item, ); + item.spaceColor && + viewer.model.getInstanceTree().enumNodeChildren( + item.forgeID, + (dbId) => { + console.log("Found dbId:", dbId); + viewer.setThemingColor(dbId, new THREE.Vector4(1, 0, 0, 1)) // RGBA (紅色)); + viewer.impl.invalidate(true); + }, + true, + ); + + // console.log(forgeViewerRef.value.Viewpoints[7]); // changeCameraView(forgeViewerRef.value.forgeViewer, forgeViewerRef.value.Viewpoints[7].data); }; @@ -184,6 +231,6 @@ watch( - + diff --git a/wwwroot/ibms_ems/src/components/ForgeLabel.vue b/wwwroot/ibms_ems/src/components/ForgeLabel.vue index 80c5081..d7e34d5 100644 --- a/wwwroot/ibms_ems/src/components/ForgeLabel.vue +++ b/wwwroot/ibms_ems/src/components/ForgeLabel.vue @@ -1,12 +1,12 @@