From 61a8117b255926ede63ea083733b21e43fc841ef Mon Sep 17 00:00:00 2001
From: Junjie <fallin.jie@qq.com>
Date: 星期五, 06 三月 2026 16:56:37 +0800
Subject: [PATCH] #

---
 src/main/webapp/components/MapCanvas.js |  157 +++++++++++++++++++++++++++++++++++++++++++++++++--
 1 files changed, 149 insertions(+), 8 deletions(-)

diff --git a/src/main/webapp/components/MapCanvas.js b/src/main/webapp/components/MapCanvas.js
index 92d3367..4554b71 100644
--- a/src/main/webapp/components/MapCanvas.js
+++ b/src/main/webapp/components/MapCanvas.js
@@ -23,6 +23,7 @@
       <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;">
+          <button type="button" @click="toggleStationDirection" style="padding: 2px 8px; font-size: 12px; cursor: pointer;">{{ showStationDirection ? '闅愯棌绔欑偣鏂瑰悜' : '鏄剧ず绔欑偣鏂瑰悜' }}</button>
           <button type="button" @click="rotateMap" style="padding: 2px 8px; font-size: 12px; cursor: pointer;">鏃嬭浆</button>
           <button type="button" @click="toggleMirror" style="padding: 2px 8px; font-size: 12px; cursor: pointer;">{{ mapMirrorX ? '鍙栨秷闀滃儚' : '闀滃儚' }}</button>
         </div>
@@ -104,9 +105,11 @@
         taskStationCount: 0,
         currentLoad: 0
       },
+      showStationDirection: false,
       hoverLoopNo: null,
       hoverLoopStationIdSet: new Set(),
-      loopHighlightColor: 0xfff34d
+      loopHighlightColor: 0xfff34d,
+      stationDirectionColor: 0xff5a36
     }
   },
     mounted() {
@@ -478,6 +481,14 @@
             }
           }
           xCursor += cellWidth;
+        }
+      });
+
+      map.forEach((row, rowIndex) => {
+        for (let colIndex = 0; colIndex < row.length; colIndex++) {
+          const val = row[colIndex];
+          if (!val || val.type !== 'devp' || val.type === 'merge') { continue; }
+          val.stationDirectionList = this.resolveStationDirectionList(map, rowIndex, colIndex, val);
         }
       });
 
@@ -1250,6 +1261,13 @@
         }
         sprite = new PIXI.Sprite(texture);
         sprite._kind = 'devp';
+        const directionOverlay = this.createStationDirectionOverlay(item.width, item.height, item.stationDirectionList);
+        if (directionOverlay) {
+          directionOverlay.visible = this.showStationDirection;
+          sprite.addChild(directionOverlay);
+        }
+        sprite.directionObj = directionOverlay;
+        sprite._stationDirectionList = Array.isArray(item.stationDirectionList) ? item.stationDirectionList.slice() : [];
         let siteId = this.getStationId(value);
         if (siteId === -1) { siteId = item.data; }
         const style = new PIXI.TextStyle({ fontFamily: 'Arial', fontSize: 10, fill: '#000000', stroke: '#ffffff', strokeThickness: 1 });
@@ -1543,6 +1561,132 @@
     },
     getStationId(obj) {
       if (this.isJson(obj)) { let data = JSON.parse(obj); if (data.stationId == null || data.stationId == undefined) { return -1; } return data.stationId; } else { return -1; }
+    },
+    parseMapValue(obj) {
+      if (obj == null) { return null; }
+      if (typeof obj === 'object') { return obj; }
+      if (!this.isJson(obj)) { return null; }
+      try {
+        return JSON.parse(obj);
+      } catch (e) {
+        return null;
+      }
+    },
+    normalizeDirectionList(direction) {
+      const aliasMap = {
+        top: 'top',
+        up: 'top',
+        north: 'top',
+        bottom: 'bottom',
+        down: 'bottom',
+        south: 'bottom',
+        left: 'left',
+        west: 'left',
+        right: 'right',
+        east: 'right'
+      };
+      let rawList = [];
+      if (Array.isArray(direction)) {
+        rawList = direction;
+      } else if (typeof direction === 'string') {
+        rawList = direction.split(/[,\s|/]+/);
+      }
+      const result = [];
+      const seen = new Set();
+      rawList.forEach((item) => {
+        const key = aliasMap[String(item || '').trim().toLowerCase()];
+        if (!key || seen.has(key)) { return; }
+        seen.add(key);
+        result.push(key);
+      });
+      return result;
+    },
+    resolveStationDirectionList(map, rowIndex, colIndex, item) {
+      const valueObj = this.parseMapValue(item && item.value);
+      const fromValue = this.normalizeDirectionList(valueObj && valueObj.direction);
+      if (fromValue.length > 0) { return fromValue; }
+      const rowSpan = item && item.rowSpan ? item.rowSpan : 1;
+      const colSpan = item && item.colSpan ? item.colSpan : 1;
+      const fallback = [];
+      const candidateList = [
+        { key: 'top', cell: this.resolveMergedCell(map, rowIndex - 1, colIndex) },
+        { key: 'right', cell: this.resolveMergedCell(map, rowIndex, colIndex + colSpan) },
+        { key: 'bottom', cell: this.resolveMergedCell(map, rowIndex + rowSpan, colIndex) },
+        { key: 'left', cell: this.resolveMergedCell(map, rowIndex, colIndex - 1) }
+      ];
+      candidateList.forEach((candidate) => {
+        if (this.isStationDirectionNeighbor(candidate.cell)) {
+          fallback.push(candidate.key);
+        }
+      });
+      return fallback;
+    },
+    isStationDirectionNeighbor(cell) {
+      if (!cell) { return false; }
+      if (cell.type === 'devp') { return true; }
+      return this.isTrackType(cell);
+    },
+    createStationDirectionOverlay(width, height, directionList) {
+      if (!Array.isArray(directionList) || directionList.length === 0) { return null; }
+      const container = new PIXI.Container();
+      const arrowSize = Math.max(4, Math.min(width, height) * 0.22);
+      const margin = Math.max(2, Math.min(width, height) * 0.12);
+      directionList.forEach((direction) => {
+        const arrow = new PIXI.Graphics();
+        this.drawStationDirectionArrow(arrow, width, height, direction, arrowSize, margin);
+        container.addChild(arrow);
+      });
+      return container;
+    },
+    drawStationDirectionArrow(graphics, width, height, direction, size, margin) {
+      if (!graphics) { return; }
+      const halfBase = Math.max(2, size * 0.45);
+      const stemLen = Math.max(3, size * 0.7);
+      const centerX = width / 2;
+      const centerY = height / 2;
+      graphics.beginFill(this.stationDirectionColor, 0.95);
+      if (direction === 'top') {
+        const tipY = margin;
+        const baseY = margin + size;
+        const stemY = Math.min(centerY - 2, baseY + stemLen);
+        if (stemY > baseY) { graphics.moveTo(centerX, stemY); graphics.lineTo(centerX, baseY); }
+        graphics.moveTo(centerX, tipY);
+        graphics.lineTo(centerX - halfBase, baseY);
+        graphics.lineTo(centerX + halfBase, baseY);
+      } else if (direction === 'right') {
+        const tipX = width - margin;
+        const baseX = width - margin - size;
+        const stemX = Math.max(centerX + 2, baseX - stemLen);
+        if (stemX < baseX) { graphics.moveTo(stemX, centerY); graphics.lineTo(baseX, centerY); }
+        graphics.moveTo(tipX, centerY);
+        graphics.lineTo(baseX, centerY - halfBase);
+        graphics.lineTo(baseX, centerY + halfBase);
+      } else if (direction === 'bottom') {
+        const tipY = height - margin;
+        const baseY = height - margin - size;
+        const stemY = Math.max(centerY + 2, baseY - stemLen);
+        if (stemY < baseY) { graphics.moveTo(centerX, stemY); graphics.lineTo(centerX, baseY); }
+        graphics.moveTo(centerX, tipY);
+        graphics.lineTo(centerX - halfBase, baseY);
+        graphics.lineTo(centerX + halfBase, baseY);
+      } else if (direction === 'left') {
+        const tipX = margin;
+        const baseX = margin + size;
+        const stemX = Math.min(centerX - 2, baseX + stemLen);
+        if (stemX > baseX) { graphics.moveTo(stemX, centerY); graphics.lineTo(baseX, centerY); }
+        graphics.moveTo(tipX, centerY);
+        graphics.lineTo(baseX, centerY - halfBase);
+        graphics.lineTo(baseX, centerY + halfBase);
+      }
+      graphics.closePath();
+      graphics.endFill();
+    },
+    applyStationDirectionVisibility() {
+      if (!this.pixiStaMap) { return; }
+      this.pixiStaMap.forEach((sprite) => {
+        if (!sprite || !sprite.directionObj) { return; }
+        sprite.directionObj.visible = this.showStationDirection;
+      });
     },
     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; }
@@ -1866,6 +2010,10 @@
       this.applyMapTransform(true);
       this.saveMapTransformConfig();
     },
+    toggleStationDirection() {
+      this.showStationDirection = !this.showStationDirection;
+      this.applyStationDirectionVisibility();
+    },
     toggleMirror() {
       this.mapMirrorX = !this.mapMirrorX;
       this.applyMapTransform(true);
@@ -2013,13 +2161,6 @@
     }
   }
 });
-
-
-
-
-
-
-
 
 
 

--
Gitblit v1.9.1