From 90cf24d60788e9228f02ffd96539d09c9b1c6bd1 Mon Sep 17 00:00:00 2001
From: skyouc <958836976@qq.com>
Date: 星期五, 06 二月 2026 17:42:11 +0800
Subject: [PATCH] 更改

---
 src/main/webapp/components/MapCanvas.js |  389 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 384 insertions(+), 5 deletions(-)

diff --git a/src/main/webapp/components/MapCanvas.js b/src/main/webapp/components/MapCanvas.js
index c87a755..3147540 100644
--- a/src/main/webapp/components/MapCanvas.js
+++ b/src/main/webapp/components/MapCanvas.js
@@ -2,6 +2,10 @@
   template: `
     <div style="width: 100%; height: 100%; position: relative;">
       <div ref="pixiView"></div>
+      <div v-show="shelfTooltip.visible"
+           :style="shelfTooltipStyle()">
+        {{ shelfTooltip.text }}
+      </div>
       <div style="position: absolute; top: 20px; right: 50px; text-align: right;">
         <div>FPS:{{mapFps}}</div>
         <div style="margin-top: 6px; display: flex; gap: 6px; justify-content: flex-end;">
@@ -46,6 +50,19 @@
       crnList: [],
       dualCrnList: [],
       rgvList: [],
+      locListMap: new Map(),
+      locListLoaded: false,
+      locListLoading: false,
+      mapRowOffsets: [],
+      mapRowHeights: [],
+      mapColOffsets: [],
+      mapColWidths: [],
+      mapRowColOffsets: [],
+      mapRowColWidths: [],
+      mapRowShelfCells: [],
+      hoveredShelfCell: null,
+      hoverPointer: { x: 0, y: 0 },
+      hoverRaf: null,
       objectsContainer: null,
       objectsContainer2: null,
       tracksContainer: null,
@@ -55,6 +72,14 @@
       graphicsCrnTrack: null,
       graphicsRgvTrack: null,
       graphicsRgv: null,
+      shelfTooltip: {
+        visible: false,
+        x: 0,
+        y: 0,
+        text: '',
+        item: null
+      },
+      shelfTooltipMinScale: 0.4,
       timer: null,
       adjustLabelTimer: null,
       isSwitchingFloor: false
@@ -64,6 +89,7 @@
     this.currentLev = this.lev || 1;
     this.createMap();
     this.loadMapTransformConfig();
+    this.loadLocList();
     this.connectWs();
     
     setTimeout(() => {
@@ -79,6 +105,8 @@
   },
   beforeDestroy() {
     if (this.timer) { clearInterval(this.timer); }
+
+    if (this.hoverRaf) { cancelAnimationFrame(this.hoverRaf); this.hoverRaf = null; }
     if (this.pixiApp) { this.pixiApp.destroy(true, { children: true }); }
     window.removeEventListener('resize', this.resizeToContainer);
     if (this.wsReconnectTimer) { clearTimeout(this.wsReconnectTimer); this.wsReconnectTimer = null; }
@@ -158,9 +186,30 @@
       this.mapRoot.addChild(this.objectsContainer);
       this.mapRoot.addChild(this.objectsContainer2);
       this.pixiApp.renderer.roundPixels = true;
+      this.hoveredShelfCell = null;
+      this.hoverPointer = { x: 0, y: 0 };
+      this.hoverRaf = null;
       
 
-      //*******************鎷栧姩鐢诲竷*******************
+
+      //*******************shelf hover*******************
+      this.pixiApp.renderer.plugins.interaction.on('pointermove', (event) => {
+        if (!this.isShelfTooltipAllowed()) { this.hideShelfTooltip(); return; }
+        if (!this.map || !this.mapRoot) { return; }
+        const pos = event.data.global;
+        this.hoverPointer.x = pos.x;
+        this.hoverPointer.y = pos.y;
+        if (this.hoverRaf) { return; }
+        this.hoverRaf = requestAnimationFrame(() => {
+          this.hoverRaf = null;
+          this.updateShelfHoverFromPointer(this.hoverPointer);
+        });
+      });
+      this.pixiApp.view.addEventListener('mouseleave', () => {
+        this.hoveredShelfCell = null;
+        this.hideShelfTooltip();
+      });
+      //*******************shelf hover*******************
       let stageOriginalPos;
       let mouseDownPoint;
       let touchBlank = false;
@@ -168,7 +217,7 @@
         const globalPos = event.data.global;
         stageOriginalPos = [this.pixiApp.stage.position.x, this.pixiApp.stage.position.y];
         mouseDownPoint = [globalPos.x, globalPos.y];
-        if (!event.target) { touchBlank = true; }
+        if (!event.target || (event.target && event.target._kind === 'shelf')) { touchBlank = true; }
       });
       this.pixiApp.renderer.plugins.interaction.on('pointermove', (event) => {
         const globalPos = event.data.global;
@@ -179,7 +228,7 @@
         }
       });
       this.pixiApp.renderer.plugins.interaction.on('pointerup', () => { touchBlank = false; });
-      //*******************鎷栧姩鐢诲竷*******************
+
 
       //*******************缂╂斁鐢诲竷*******************
       this.pixiApp.view.addEventListener('wheel', (event) => {
@@ -241,6 +290,12 @@
     changeFloor(lev) {
       this.currentLev = lev;
       this.isSwitchingFloor = true;
+      this.hideShelfTooltip();
+      this.hoveredShelfCell = null;
+      this.mapRowOffsets = [];
+      this.mapRowHeights = [];
+      this.mapColOffsets = [];
+      this.mapColWidths = [];
       if (this.adjustLabelTimer) { clearTimeout(this.adjustLabelTimer); this.adjustLabelTimer = null; }
       this.objectsContainer.removeChildren();
       this.objectsContainer2.removeChildren();
@@ -258,6 +313,12 @@
       this.getMap();
     },
     createMapData(map) {
+      this.hideShelfTooltip();
+      this.hoveredShelfCell = null;
+      this.mapRowOffsets = [];
+      this.mapRowHeights = [];
+      this.mapColOffsets = [];
+      this.mapColWidths = [];
       if (window.gsap) {
         this.pixiStaMap && this.pixiStaMap.forEach((s) => { try { window.gsap.killTweensOf(s); } catch (e) {} });
         this.pixiCrnMap && this.pixiCrnMap.forEach((s) => { try { window.gsap.killTweensOf(s); } catch (e) {} });
@@ -347,6 +408,8 @@
           let val = row[colIndex];
           let cellWidth = val.width;
           let cellHeight = val.height;
+          val.rowIndex = rowIndex;
+          val.colIndex = colIndex;
           if (val.isMergedPart) {
             val.posX = anchorX;
             val.posY = yOffsets[rowIndex];
@@ -367,12 +430,16 @@
         }
       });
 
+      this.buildShelfHitGrid(map, rowHeightScaled, yOffsets);
+
       this.drawTracks(map);
 
       map.forEach((item, index) => {
         this.pixiStageList[index] = [item.length];
         for (let idx = 0; idx < item.length; idx++) {
           let val = item[idx];
+          val.rowIndex = index;
+          val.colIndex = idx;
           if (val.type === 'merge') { continue; }
           if (val.type == undefined || val.type === 'none') { continue; }
           if (this.isTrackType(val)) {
@@ -406,7 +473,7 @@
         sprite.textObj = text;
         sprite.position.set(item.posX, item.posY);
         sprite.interactive = true; // 蹇呴』瑕佽缃墠鑳芥帴鏀朵簨浠�
-        sprite.buttonMode = true; // 璁╁厜鏍囧湪hover鏃跺彉涓烘墜鍨嬫寚閽�
+        sprite.buttonMode = true; // 璁╁厜鏍囧湪hover鏃跺彉涓烘墜鍨嬫寚浜嬩欢
         sprite.on('pointerdown', () => {
           if (window.gsap) { window.gsap.killTweensOf(sprite); }
           sprite.alpha = 1;
@@ -472,7 +539,7 @@
         sprite.textObj = text;
         sprite.position.set(item.posX, item.posY);
         sprite.interactive = true; // 蹇呴』瑕佽缃墠鑳芥帴鏀朵簨浠�
-        sprite.buttonMode = true; // 璁╁厜鏍囧湪hover鏃跺彉涓烘墜鍨嬫寚閽�
+        sprite.buttonMode = true; // 璁╁厜鏍囧湪hover鏃跺彉涓烘墜鍨嬫寚浜嬩欢
         sprite.on('pointerdown', () => {
           if (window.gsap) { window.gsap.killTweensOf(sprite); }
           sprite.alpha = 1;
@@ -1325,6 +1392,253 @@
     getTrackSiteNo(obj) {
       if (this.isJson(obj)) { let data = JSON.parse(obj); if (data.trackSiteNo == null || data.trackSiteNo == undefined) { return -1; } return data.trackSiteNo; } else { return -1; }
     },
+    buildShelfHitGrid(map, rowHeights, rowOffsets) {
+      if (!map || !Array.isArray(map)) { return; }
+      this.mapRowOffsets = Array.isArray(rowOffsets) ? rowOffsets.slice() : [];
+      this.mapRowHeights = Array.isArray(rowHeights) ? rowHeights.slice() : [];
+      const rowColOffsets = [];
+      const rowColWidths = [];
+      const rowShelfCells = new Array(map.length);
+      let maxCols = 0;
+      for (let r = 0; r < map.length; r++) {
+        const row = map[r];
+        if (row && row.length > maxCols) { maxCols = row.length; }
+        rowShelfCells[r] = [];
+      }
+      const colWidths = new Array(maxCols);
+      for (let c = 0; c < maxCols; c++) {
+        let w = null;
+        for (let r = 0; r < map.length; r++) {
+          const cell = map[r] && map[r][c];
+          if (!cell) { continue; }
+          if (cell.cellWidth != null && cell.cellWidth !== '') {
+            const base = Number(cell.cellWidth);
+            if (isFinite(base) && base > 0) { w = base / 40; break; }
+          }
+        }
+        colWidths[c] = (w && isFinite(w) && w > 0) ? w : 25;
+      }
+      const colOffsets = new Array(maxCols);
+      let xCursor = 0;
+      for (let c = 0; c < maxCols; c++) {
+        colOffsets[c] = xCursor;
+        xCursor += colWidths[c];
+      }
+      for (let r = 0; r < map.length; r++) {
+        const row = map[r];
+        if (!row || row.length === 0) {
+          rowColOffsets[r] = [];
+          rowColWidths[r] = [];
+          continue;
+        }
+        const widths = new Array(row.length);
+        for (let c = 0; c < row.length; c++) {
+          const cell = row[c];
+          let w = null;
+          if (cell && cell.cellWidth != null && cell.cellWidth !== '') {
+            const base = Number(cell.cellWidth);
+            if (isFinite(base) && base > 0) { w = base / 40; }
+          }
+          widths[c] = (w && isFinite(w) && w > 0) ? w : 25;
+        }
+        const offsets = new Array(row.length);
+        let x = 0;
+        for (let c = 0; c < row.length; c++) {
+          offsets[c] = x;
+          x += widths[c];
+        }
+        rowColOffsets[r] = offsets;
+        rowColWidths[r] = widths;
+      }
+      this.mapColWidths = colWidths;
+      this.mapColOffsets = colOffsets;
+      this.mapRowColOffsets = rowColOffsets;
+      this.mapRowColWidths = rowColWidths;
+      this.mapRowShelfCells = rowShelfCells;
+
+      for (let r = 0; r < map.length; r++) {
+        const row = map[r];
+        if (!row) { continue; }
+        for (let c = 0; c < row.length; c++) {
+          const cell = row[c];
+          if (!cell || cell.type !== 'shelf') { continue; }
+          const startRow = this.findIndexByOffsets(this.mapRowOffsets, this.mapRowHeights, cell.posY + 0.01);
+          const endRow = this.findIndexByOffsets(this.mapRowOffsets, this.mapRowHeights, cell.posY + cell.height - 0.01);
+          if (startRow < 0) { continue; }
+          const last = endRow >= 0 ? endRow : startRow;
+          for (let rr = startRow; rr <= last; rr++) {
+            if (!rowShelfCells[rr]) { rowShelfCells[rr] = []; }
+            rowShelfCells[rr].push(cell);
+          }
+        }
+      }
+    },
+    findIndexByOffsets(offsets, sizes, value) {
+      if (!offsets || !sizes || offsets.length === 0) { return -1; }
+      for (let i = 0; i < offsets.length; i++) {
+        const start = offsets[i];
+        const end = start + (sizes[i] || 0);
+        if (value >= start && value < end) { return i; }
+      }
+      return -1;
+    },
+    updateShelfHoverFromPointer(globalPos) {
+      if (!this.map || !this.mapRoot) { return; }
+      if (!this.mapRowOffsets.length || !this.mapColOffsets.length) { return; }
+      const local = this.mapRoot.toLocal(new PIXI.Point(globalPos.x, globalPos.y));
+      const rowIndex = this.findIndexByOffsets(this.mapRowOffsets, this.mapRowHeights, local.y);
+      if (rowIndex < 0) { if (this.hoveredShelfCell) { this.hoveredShelfCell = null; this.hideShelfTooltip(); } return; }
+      let cell = null;
+      if (this.mapRowShelfCells && this.mapRowShelfCells[rowIndex]) {
+        const list = this.mapRowShelfCells[rowIndex];
+        for (let i = 0; i < list.length; i++) {
+          const it = list[i];
+          if (!it) { continue; }
+          if (local.x >= it.posX && local.x < it.posX + it.width &&
+              local.y >= it.posY && local.y < it.posY + it.height) {
+            cell = it;
+            break;
+          }
+        }
+      }
+      if (!cell || cell.type !== 'shelf') { if (this.hoveredShelfCell) { this.hoveredShelfCell = null; this.hideShelfTooltip(); } return; }
+      if (this.hoveredShelfCell !== cell) {
+        this.hoveredShelfCell = cell;
+        this.shelfTooltip.item = cell;
+        this.shelfTooltip.text = this.getShelfArrangeInfo(cell);
+        this.shelfTooltip.visible = true;
+      }
+      this.updateShelfTooltipPositionByGlobal(globalPos);
+    },
+    normalizeLocTypeKey(value) {
+      if (value == null) { return null; }
+      const str = String(value).trim();
+      if (!str) { return null; }
+      const parts = str.split('-').filter(p => p !== '');
+      if (parts.length >= 3) { return parts.slice(0, parts.length - 1).join('-'); }
+      return str;
+    },
+    loadLocList() {
+      if (!window.$ || typeof baseUrl === 'undefined') { return; }
+      if (this.locListLoading) { return; }
+      this.locListLoading = true;
+      $.ajax({
+        url: baseUrl + "/console/map/locList",
+        headers: { 'token': localStorage.getItem('token') },
+        dataType: 'json',
+        method: 'GET',
+        success: (res) => {
+          if (res && !Array.isArray(res)) {
+            if (res.code === 403) { parent.location.href = baseUrl + "/login"; return; }
+            if (res.code !== 200) { return; }
+          }
+          const list = Array.isArray(res) ? res : (res && res.code === 200 ? res.data : null);
+          if (!list || !Array.isArray(list)) { return; }
+          const map = new Map();
+          list.forEach((item) => {
+            if (!item) { return; }
+            const locType = item.locType != null ? item.locType : item.loc_type;
+            if (locType != null && locType !== '') {
+              const normalizedType = this.normalizeLocTypeKey(locType);
+              if (normalizedType && !map.has(normalizedType)) { map.set(normalizedType, item); }
+            }
+          });
+          this.locListMap = map;
+          this.locListLoaded = true;
+          if (this.shelfTooltip.visible) {
+            this.shelfTooltip.text = this.getShelfArrangeInfo(this.shelfTooltip.item);
+          }
+        },
+        complete: () => {
+          this.locListLoading = false;
+        }
+      });
+    },
+    showShelfTooltip(e, item) {
+      if (!item) { return; }
+      if (!this.isShelfTooltipAllowed()) { this.hideShelfTooltip(); return; }
+      if (!this.locListLoaded && !this.locListLoading) { this.loadLocList(); }
+      this.shelfTooltip.item = item;
+      this.shelfTooltip.text = this.getShelfArrangeInfo(item);
+      this.updateShelfTooltipPosition(e);
+      this.shelfTooltip.visible = true;
+    },
+    updateShelfTooltipPosition(e) {
+      if (!e || !e.data || !e.data.global) { return; }
+      this.updateShelfTooltipPositionByGlobal(e.data.global);
+    },
+    updateShelfTooltipPositionByGlobal(globalPos) {
+      if (!this.isShelfTooltipAllowed()) { this.hideShelfTooltip(); return; }
+      if (!globalPos) { return; }
+      this.shelfTooltip.x = globalPos.x + 12;
+      this.shelfTooltip.y = globalPos.y + 12;
+    },
+    hideShelfTooltip() {
+      this.shelfTooltip.visible = false;
+      this.shelfTooltip.item = null;
+    },
+    isShelfTooltipAllowed() {
+      return this.getStageAbsScale() >= this.shelfTooltipMinScale;
+    },
+    getStageAbsScale() {
+      if (!this.pixiApp || !this.pixiApp.stage) { return 1; }
+      return Math.abs(this.pixiApp.stage.scale.x || 1);
+    },
+    updateShelfTooltipVisibilityByScale() {
+      if (this.shelfTooltip.visible && !this.isShelfTooltipAllowed()) {
+        this.hideShelfTooltip();
+        this.hoveredShelfCell = null;
+      }
+    },
+    getShelfArrangeInfo(item) {
+      const parts = [];
+      const matchKey = this.getShelfMatchKey(item);
+      if (matchKey != null) { parts.push('鍧愭爣:' + matchKey); }
+      const locInfo = (matchKey != null) ? this.locListMap.get(matchKey) : null;
+      if (locInfo) {
+        const locNo = locInfo.locNo != null ? locInfo.locNo : locInfo.loc_no;
+        const displayLocNo = this.stripLocLayer(locNo);
+        if (displayLocNo != null) { parts.push('鎺掑垪:' + displayLocNo); }
+      }
+      return parts.join('  ');
+    },
+    getShelfMatchKey(item) {
+      if (!item) { return null; }
+      const direct = item.locType != null ? item.locType : (item.loc_type != null ? item.loc_type : null);
+      const directKey = this.normalizeLocTypeKey(direct);
+      if (directKey) { return directKey; }
+      const rowIndex = item.rowIndex;
+      const colIndex = item.colIndex;
+      if (rowIndex == null || colIndex == null) { return null; }
+      const key0 = rowIndex + '-' + colIndex;
+      if (this.locListLoaded && this.locListMap && this.locListMap.size > 0) {
+        if (this.locListMap.has(key0)) { return key0; }
+      }
+      return null;
+    },
+    stripLocLayer(locNo) {
+      if (locNo == null) { return null; }
+      const str = String(locNo).trim();
+      if (!str) { return null; }
+      const parts = str.split('-').filter(p => p !== '');
+      if (parts.length >= 3) { return parts.slice(0, parts.length - 1).join('-'); }
+      return str;
+    },
+    shelfTooltipStyle() {
+      return {
+        position: 'absolute',
+        left: this.shelfTooltip.x + 'px',
+        top: this.shelfTooltip.y + 'px',
+        background: 'rgba(0,0,0,0.75)',
+        color: '#ffffff',
+        padding: '4px 8px',
+        borderRadius: '4px',
+        fontSize: '12px',
+        pointerEvents: 'none',
+        whiteSpace: 'nowrap',
+        zIndex: 10
+      };
+    },
     adjustLabelScale() {
       const s = this.pixiApp && this.pixiApp.stage ? Math.abs(this.pixiApp.stage.scale.x || 1) : 1;
       const minPx = 14;
@@ -1536,8 +1850,73 @@
       if (this.adjustLabelTimer) { clearTimeout(this.adjustLabelTimer); }
       this.adjustLabelTimer = setTimeout(() => {
         this.adjustLabelScale();
+        this.updateShelfTooltipVisibilityByScale();
         this.adjustLabelTimer = null;
       }, 20);
     }
   }
 });
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

--
Gitblit v1.9.1