#
Junjie
21 小时以前 3c37636d4632aeaba6d17f67d89f14a9c208ab33
src/main/webapp/components/MapCanvas.js
@@ -1,7 +1,21 @@
Vue.component('map-canvas', {
  template: `
    <div style="width: 100%; height: 100%; position: relative;">
      <div ref="pixiView"></div>
      <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 }}
@@ -80,14 +94,25 @@
        item: null
      },
      shelfTooltipMinScale: 0.4,
      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() {
    this.currentLev = this.lev || 1;
    this.createMap();
    this.startContainerResizeObserve();
    this.loadMapTransformConfig();
    this.loadLocList();
    this.connectWs();
@@ -100,6 +125,7 @@
      this.getCrnInfo();
      this.getDualCrnInfo();
      this.getSiteInfo();
      this.getCycleCapacityInfo();
      this.getRgvInfo();
    }, 1000);
  },
@@ -108,6 +134,7 @@
    if (this.hoverRaf) { cancelAnimationFrame(this.hoverRaf); this.hoverRaf = null; }
    if (this.pixiApp) { this.pixiApp.destroy(true, { children: true }); }
    if (this.containerResizeObserver) { this.containerResizeObserver.disconnect(); this.containerResizeObserver = null; }
    window.removeEventListener('resize', this.resizeToContainer);
    if (this.wsReconnectTimer) { clearTimeout(this.wsReconnectTimer); this.wsReconnectTimer = null; }
    if (this.ws && (this.ws.readyState === WebSocket.OPEN || this.ws.readyState === WebSocket.CONNECTING)) { try { this.ws.close(); } catch (e) {} }
@@ -277,11 +304,33 @@
      });
      //*******************FPS*******************
    },
    startContainerResizeObserve() {
      if (typeof ResizeObserver === 'undefined' || !this.$el) { return; }
      this.containerResizeObserver = new ResizeObserver(() => {
        this.resizeToContainer();
      });
      this.containerResizeObserver.observe(this.$el);
    },
    getViewportSize() {
      if (!this.pixiApp || !this.pixiApp.renderer) { return { width: 0, height: 0 }; }
      const screen = this.pixiApp.renderer.screen;
      if (screen && screen.width > 0 && screen.height > 0) {
        return { width: screen.width, height: screen.height };
      }
      const rect = this.pixiApp.view ? this.pixiApp.view.getBoundingClientRect() : null;
      return { width: rect ? rect.width : 0, height: rect ? rect.height : 0 };
    },
    resizeToContainer() {
      const w = this.$el.clientWidth || 0;
      const h = this.$el.clientHeight || 0;
      if (w > 0 && h > 0 && this.pixiApp) {
        const vw = this.pixiApp.renderer && this.pixiApp.renderer.screen ? this.pixiApp.renderer.screen.width : 0;
        const vh = this.pixiApp.renderer && this.pixiApp.renderer.screen ? this.pixiApp.renderer.screen.height : 0;
        if (vw === w && vh === h) { return; }
        this.pixiApp.renderer.resize(w, h);
        if (this.mapContentSize && this.mapContentSize.width > 0 && this.mapContentSize.height > 0) {
          this.applyMapTransform(true);
        }
      }
    },
    getMap() {
@@ -289,6 +338,7 @@
    },
    changeFloor(lev) {
      this.currentLev = lev;
      this.clearLoopStationHighlight();
      this.isSwitchingFloor = true;
      this.hideShelfTooltip();
      this.hoveredShelfCell = null;
@@ -313,6 +363,7 @@
      this.getMap();
    },
    createMapData(map) {
      this.clearLoopStationHighlight();
      this.hideShelfTooltip();
      this.hoveredShelfCell = null;
      this.mapRowOffsets = [];
@@ -627,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() {
@@ -659,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);
@@ -814,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();
@@ -828,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));
      }
@@ -1175,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', () => {
@@ -1376,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; }
@@ -1642,8 +1800,9 @@
    adjustLabelScale() {
      const s = this.pixiApp && this.pixiApp.stage ? Math.abs(this.pixiApp.stage.scale.x || 1) : 1;
      const minPx = 14;
      const vw = this.pixiApp.view.width;
      const vh = this.pixiApp.view.height;
      const viewport = this.getViewportSize();
      const vw = viewport.width;
      const vh = viewport.height;
      const margin = 50;
      const mirrorSign = this.mapMirrorX ? -1 : 1;
      const inverseRotation = -((this.mapRotation % 360) * Math.PI / 180);
@@ -1821,8 +1980,9 @@
      const contentW = size.width || 0;
      const contentH = size.height || 0;
      if (contentW <= 0 || contentH <= 0) { return; }
      const vw = this.pixiApp.view.width;
      const vh = this.pixiApp.view.height;
      const viewport = this.getViewportSize();
      const vw = viewport.width;
      const vh = viewport.height;
      let scale = Math.min(vw / contentW, vh / contentH) * 0.95;
      if (!isFinite(scale) || scale <= 0) { scale = 1; }
      const baseW = this.mapContentSize.width || contentW;
@@ -1856,12 +2016,6 @@
    }
  }
});