From 0b1cc54f62919c5face9794208a0d24da0f97293 Mon Sep 17 00:00:00 2001
From: Junjie <fallin.jie@qq.com>
Date: 星期三, 04 三月 2026 13:53:50 +0800
Subject: [PATCH] #

---
 src/main/webapp/components/MapCanvas.js |  158 +++++++++++++++++++++++++++++++++++++++++++++++-----
 1 files changed, 143 insertions(+), 15 deletions(-)

diff --git a/src/main/webapp/components/MapCanvas.js b/src/main/webapp/components/MapCanvas.js
index 795f549..f04f4ea 100644
--- a/src/main/webapp/components/MapCanvas.js
+++ b/src/main/webapp/components/MapCanvas.js
@@ -2,6 +2,20 @@
   template: `
     <div style="width: 100%; height: 100%; position: relative;">
       <div ref="pixiView" style="position: absolute; inset: 0;"></div>
+      <div style="position: absolute; top: 12px; left: 14px; z-index: 30; pointer-events: none; max-width: 52%;">
+        <div style="display: flex; flex-direction: column; gap: 6px; align-items: flex-start;">
+          <div v-for="item in cycleCapacity.loopList"
+               :key="'loop-' + item.loopNo"
+               @mouseenter="handleLoopCardEnter(item)"
+               @mouseleave="handleLoopCardLeave(item)"
+               style="padding: 6px 10px; border-radius: 4px; background: rgba(11, 35, 58, 0.72); color: #fff; font-size: 12px; line-height: 1.4; white-space: nowrap; pointer-events: auto;">
+            鍦坽{ item.loopNo }} |
+            绔欑偣: {{ item.stationCount || 0 }} |
+            浠诲姟: {{ item.taskCount || 0 }} |
+            鎵胯浇: {{ formatLoadPercent(item.currentLoad) }}
+          </div>
+        </div>
+      </div>
       <div v-show="shelfTooltip.visible"
            :style="shelfTooltipStyle()">
         {{ shelfTooltip.text }}
@@ -83,7 +97,16 @@
       containerResizeObserver: null,
       timer: null,
       adjustLabelTimer: null,
-      isSwitchingFloor: false
+      isSwitchingFloor: false,
+      cycleCapacity: {
+        loopList: [],
+        totalStationCount: 0,
+        taskStationCount: 0,
+        currentLoad: 0
+      },
+      hoverLoopNo: null,
+      hoverLoopStationIdSet: new Set(),
+      loopHighlightColor: 0xfff34d
     }
   },
     mounted() {
@@ -102,6 +125,7 @@
       this.getCrnInfo();
       this.getDualCrnInfo();
       this.getSiteInfo();
+      this.getCycleCapacityInfo();
       this.getRgvInfo();
     }, 1000);
   },
@@ -314,6 +338,7 @@
     },
     changeFloor(lev) {
       this.currentLev = lev;
+      this.clearLoopStationHighlight();
       this.isSwitchingFloor = true;
       this.hideShelfTooltip();
       this.hoveredShelfCell = null;
@@ -338,6 +363,7 @@
       this.getMap();
     },
     createMapData(map) {
+      this.clearLoopStationHighlight();
       this.hideShelfTooltip();
       this.hoveredShelfCell = null;
       this.mapRowOffsets = [];
@@ -652,21 +678,21 @@
           sta.statusObj = null;
           if (sta.textObj.parent !== sta) { sta.addChild(sta.textObj); sta.textObj.position.set(sta.width / 2, sta.height / 2); }
         }
+        let baseColor = 0xb8b8b8;
         if (status === "site-auto") {
-          this.updateColor(sta, 0x78ff81);
+          baseColor = 0x78ff81;
         } else if (status === "site-auto-run" || status === "site-auto-id" || status === "site-auto-run-id") {
-          this.updateColor(sta, 0xfa51f6);
+          baseColor = 0xfa51f6;
         } else if (status === "site-unauto") {
-          this.updateColor(sta, 0xb8b8b8);
+          baseColor = 0xb8b8b8;
         } else if (status === "machine-pakin") {
-          this.updateColor(sta, 0x30bffc);
+          baseColor = 0x30bffc;
         } else if (status === "machine-pakout") {
-          this.updateColor(sta, 0x97b400);
+          baseColor = 0x97b400;
         } else if (status === "site-run-block") {
-          this.updateColor(sta, 0xe69138);
-        } else {
-          this.updateColor(sta, 0xb8b8b8);
+          baseColor = 0xe69138;
         }
+        this.setStationBaseColor(sta, baseColor);
       });
     },
     getCrnInfo() {
@@ -684,6 +710,38 @@
     getRgvInfo() {
       if (this.isSwitchingFloor) { return; }
       this.sendWs(JSON.stringify({ url: "/console/latest/data/rgv", data: {} }));
+    },
+    getCycleCapacityInfo() {
+      if (this.isSwitchingFloor) { return; }
+      this.sendWs(JSON.stringify({ url: "/console/latest/data/station/cycle/capacity", data: {} }));
+    },
+    setCycleCapacityInfo(res) {
+      const payload = res && res.code === 200 ? res.data : null;
+      if (res && res.code === 403) { parent.location.href = baseUrl + "/login"; return; }
+      if (!payload) { return; }
+      const loopList = Array.isArray(payload.loopList) ? payload.loopList : [];
+      this.cycleCapacity = {
+        loopList: loopList,
+        totalStationCount: payload.totalStationCount || 0,
+        taskStationCount: payload.taskStationCount || 0,
+        currentLoad: typeof payload.currentLoad === 'number' ? payload.currentLoad : parseFloat(payload.currentLoad || 0)
+      };
+      if (this.hoverLoopNo != null) {
+        const targetLoop = loopList.find(v => v && v.loopNo === this.hoverLoopNo);
+        if (targetLoop) {
+          this.hoverLoopStationIdSet = this.buildStationIdSet(targetLoop.stationIdList);
+          this.applyLoopStationHighlight();
+        } else {
+          this.clearLoopStationHighlight();
+        }
+      }
+    },
+    formatLoadPercent(load) {
+      let value = typeof load === 'number' ? load : parseFloat(load || 0);
+      if (!isFinite(value)) { value = 0; }
+      if (value < 0) { value = 0; }
+      if (value > 1) { value = 1; }
+      return (value * 100).toFixed(1) + "%";
     },
     setCrnInfo(res) {
       let crns = Array.isArray(res) ? res : (res && res.code === 200 ? res.data : null);
@@ -839,6 +897,7 @@
       if (this.wsReconnectTimer) { clearTimeout(this.wsReconnectTimer); this.wsReconnectTimer = null; }
       this.wsReconnectAttempts = 0;
       this.getMap(this.currentLev);
+      this.getCycleCapacityInfo();
     },
     webSocketOnError(e) {
       this.scheduleReconnect();
@@ -853,6 +912,8 @@
         this.setDualCrnInfo(JSON.parse(result.data));
       } else if (result.url === "/console/latest/data/rgv") {
         this.setRgvInfo(JSON.parse(result.data));
+      } else if (result.url === "/console/latest/data/station/cycle/capacity") {
+        this.setCycleCapacityInfo(JSON.parse(result.data));
       } else if (typeof result.url === "string" && result.url.indexOf("/basMap/lev/") === 0) {
         this.setMap(JSON.parse(result.data));
       }
@@ -1200,7 +1261,11 @@
         text.position.set(sprite.width / 2, sprite.height / 2);
         sprite.addChild(text);
         sprite.textObj = text;
-        if (siteId != null && siteId !== -1) { this.pixiStaMap.set(parseInt(siteId), sprite); }
+        const stationIdInt = parseInt(siteId, 10);
+        if (!isNaN(stationIdInt)) { this.pixiStaMap.set(stationIdInt, sprite); }
+        sprite._stationId = isNaN(stationIdInt) ? null : stationIdInt;
+        sprite._baseColor = 0x00ff7f;
+        sprite._loopHighlighted = false;
         sprite.interactive = true;
         sprite.buttonMode = true;
         sprite.on('pointerdown', () => {
@@ -1401,6 +1466,74 @@
         return;
       }
       sprite.tint = color;
+    },
+    setStationBaseColor(sprite, color) {
+      if (!sprite) { return; }
+      sprite._baseColor = color;
+      if (this.isStationInHoverLoop(sprite)) {
+        this.applyHighlightColor(sprite);
+      } else {
+        this.updateColor(sprite, color);
+        sprite._loopHighlighted = false;
+      }
+    },
+    applyHighlightColor(sprite) {
+      if (!sprite) { return; }
+      this.updateColor(sprite, this.loopHighlightColor);
+      sprite._loopHighlighted = true;
+    },
+    isStationInHoverLoop(sprite) {
+      if (!sprite || sprite._stationId == null || !this.hoverLoopStationIdSet) { return false; }
+      return this.hoverLoopStationIdSet.has(sprite._stationId);
+    },
+    buildStationIdSet(stationIdList) {
+      const set = new Set();
+      if (!Array.isArray(stationIdList)) { return set; }
+      stationIdList.forEach((id) => {
+        const v = parseInt(id, 10);
+        if (!isNaN(v)) { set.add(v); }
+      });
+      return set;
+    },
+    applyLoopStationHighlight() {
+      if (!this.pixiStaMap) { return; }
+      this.pixiStaMap.forEach((sprite) => {
+        if (!sprite) { return; }
+        if (this.isStationInHoverLoop(sprite)) {
+          this.applyHighlightColor(sprite);
+        } else if (sprite._loopHighlighted) {
+          const baseColor = (typeof sprite._baseColor === 'number') ? sprite._baseColor : 0xb8b8b8;
+          this.updateColor(sprite, baseColor);
+          sprite._loopHighlighted = false;
+        }
+      });
+    },
+    clearLoopStationHighlight() {
+      if (this.pixiStaMap) {
+        this.pixiStaMap.forEach((sprite) => {
+          if (!sprite || !sprite._loopHighlighted) { return; }
+          const baseColor = (typeof sprite._baseColor === 'number') ? sprite._baseColor : 0xb8b8b8;
+          this.updateColor(sprite, baseColor);
+          sprite._loopHighlighted = false;
+        });
+      }
+      this.hoverLoopNo = null;
+      this.hoverLoopStationIdSet = new Set();
+    },
+    handleLoopCardEnter(loopItem) {
+      if (!loopItem) { return; }
+      this.hoverLoopNo = loopItem.loopNo;
+      this.hoverLoopStationIdSet = this.buildStationIdSet(loopItem.stationIdList);
+      this.applyLoopStationHighlight();
+    },
+    handleLoopCardLeave(loopItem) {
+      if (!loopItem) {
+        this.clearLoopStationHighlight();
+        return;
+      }
+      if (this.hoverLoopNo === loopItem.loopNo) {
+        this.clearLoopStationHighlight();
+      }
     },
     isJson(str) {
       try { JSON.parse(str); return true; } catch (e) { return false; }
@@ -1883,11 +2016,6 @@
     }
   }
 });
-
-
-
-
-
 
 
 

--
Gitblit v1.9.1