Junjie
16 小时以前 be5c87afd82e50b6ef58a24e06a7a6cb36fb5007
src/main/webapp/components/MapCanvas.js
@@ -49,7 +49,7 @@
      </div>
    </div>
  `,
  props: ['lev', 'levList', 'crnParam', 'rgvParam', 'devpParam', 'stationTaskRange', 'highlightOnParamChange', 'viewportPadding', 'hudPadding'],
  props: ['lev', 'levList', 'crnParam', 'rgvParam', 'devpParam', 'stationTaskRange', 'highlightOnParamChange', 'viewportPadding', 'hudPadding', 'traceOverlay'],
  data() {
    return {
      map: [],
@@ -103,6 +103,8 @@
      hoverRaf: null,
      objectsContainer: null,
      objectsContainer2: null,
      traceOverlayContainer: null,
      tracePulseTween: null,
      tracksContainer: null,
      tracksGraphics: null,
      shelvesContainer: null,
@@ -176,6 +178,7 @@
    if (this.shelfCullRaf) { cancelAnimationFrame(this.shelfCullRaf); this.shelfCullRaf = null; }
    if (this.resizeDebounceTimer) { clearTimeout(this.resizeDebounceTimer); this.resizeDebounceTimer = null; }
    if (window.gsap && this.pixiApp && this.pixiApp.stage) { window.gsap.killTweensOf(this.pixiApp.stage.position); }
    this.clearTraceOverlay();
    if (this.pixiApp) { this.pixiApp.destroy(true, { children: true }); }
    if (this.containerResizeObserver) { this.containerResizeObserver.disconnect(); this.containerResizeObserver = null; }
    window.removeEventListener('resize', this.scheduleResizeToContainer);
@@ -234,6 +237,12 @@
            window.gsap.fromTo(sprite, { alpha: 1 }, { alpha: 0.2, yoyo: true, repeat: 6, duration: 0.15 });
          }
        }
      }
    },
    traceOverlay: {
      deep: true,
      handler() {
        this.renderTraceOverlay();
      }
    }
  },
@@ -384,6 +393,7 @@
      this.graphicsRgvTrack = this.createTrackTexture(25, 25, 10);
      this.objectsContainer = new PIXI.Container();
      this.objectsContainer2 = new PIXI.Container();
      this.traceOverlayContainer = new PIXI.Container();
      this.tracksContainer = new PIXI.ParticleContainer(10000, { scale: true, position: true, rotation: false, uvs: false, alpha: false });
      this.tracksGraphics = new PIXI.Graphics();
      this.shelvesContainer = new PIXI.Container();
@@ -395,6 +405,7 @@
      this.mapRoot.addChild(this.shelvesContainer);
      this.mapRoot.addChild(this.objectsContainer);
      this.mapRoot.addChild(this.objectsContainer2);
      this.mapRoot.addChild(this.traceOverlayContainer);
      this.pixiApp.renderer.roundPixels = true;
      this.hoveredShelfCell = null;
      this.hoverPointer = { x: 0, y: 0 };
@@ -594,6 +605,7 @@
    changeFloor(lev) {
      this.currentLev = lev;
      this.clearLoopStationHighlight();
      this.clearTraceOverlay();
      this.isSwitchingFloor = true;
      this.hideShelfTooltip();
      this.hoveredShelfCell = null;
@@ -619,6 +631,7 @@
    },
    createMapData(map) {
      this.clearLoopStationHighlight();
      this.clearTraceOverlay();
      this.hideShelfTooltip();
      this.hoveredShelfCell = null;
      this.mapRowOffsets = [];
@@ -886,6 +899,7 @@
      this.applyMapTransform(true);
      this.map = map;
      this.isSwitchingFloor = false;
      this.renderTraceOverlay();
    },
    initWidth(map) {
      let maxRow = map.length;
@@ -1816,6 +1830,129 @@
      this.hoverLoopNo = null;
      this.hoverLoopStationIdSet = new Set();
    },
    clearTraceOverlay() {
      if (window.gsap && this.tracePulseTween) {
        try { this.tracePulseTween.kill(); } catch (e) {}
      }
      this.tracePulseTween = null;
      if (!this.traceOverlayContainer) { return; }
      const children = this.traceOverlayContainer.removeChildren();
      children.forEach((child) => {
        if (child && typeof child.destroy === 'function') {
          child.destroy({ children: true, texture: false, baseTexture: false });
        }
      });
    },
    normalizeTraceOverlay(trace) {
      if (!trace) { return null; }
      const taskNo = parseInt(trace.taskNo, 10);
      return {
        taskNo: isNaN(taskNo) ? null : taskNo,
        status: trace.status || '',
        currentStationId: this.parseStationTaskNo(trace.currentStationId),
        finalTargetStationId: this.parseStationTaskNo(trace.finalTargetStationId),
        blockedStationId: this.parseStationTaskNo(trace.blockedStationId),
        passedStationIds: this.normalizeTraceStationIds(trace.passedStationIds),
        pendingStationIds: this.normalizeTraceStationIds(trace.pendingStationIds),
        latestAppendedPath: this.normalizeTraceStationIds(trace.latestAppendedPath)
      };
    },
    normalizeTraceStationIds(list) {
      if (!Array.isArray(list)) { return []; }
      const result = [];
      list.forEach((item) => {
        const stationId = parseInt(item, 10);
        if (!isNaN(stationId)) { result.push(stationId); }
      });
      return result;
    },
    getStationCenter(stationId) {
      if (stationId == null || !this.pixiStaMap) { return null; }
      const sprite = this.pixiStaMap.get(parseInt(stationId, 10));
      if (!sprite) { return null; }
      return {
        x: sprite.x + sprite.width / 2,
        y: sprite.y + sprite.height / 2
      };
    },
    drawTracePairs(graphics, stationIds, color, width, alpha) {
      if (!graphics || !Array.isArray(stationIds) || stationIds.length < 2) { return; }
      graphics.lineStyle({ width: width, color: color, alpha: alpha, cap: PIXI.LINE_CAP.ROUND, join: PIXI.LINE_JOIN.ROUND });
      for (let i = 1; i < stationIds.length; i++) {
        const prev = this.getStationCenter(stationIds[i - 1]);
        const curr = this.getStationCenter(stationIds[i]);
        if (!prev || !curr) { continue; }
        graphics.moveTo(prev.x, prev.y);
        graphics.lineTo(curr.x, curr.y);
      }
    },
    drawTraceMarker(container, stationId, options) {
      const point = this.getStationCenter(stationId);
      if (!container || !point) { return null; }
      const marker = new PIXI.Container();
      const ring = new PIXI.Graphics();
      const fill = new PIXI.Graphics();
      const radius = options && options.radius ? options.radius : 18;
      const color = options && options.color != null ? options.color : 0x1d4ed8;
      ring.lineStyle(3, color, 0.95);
      ring.drawCircle(0, 0, radius);
      fill.beginFill(color, 0.18);
      fill.drawCircle(0, 0, Math.max(6, radius * 0.42));
      fill.endFill();
      marker.addChild(ring);
      marker.addChild(fill);
      marker.position.set(point.x, point.y);
      container.addChild(marker);
      return marker;
    },
    buildPendingTraceSequence(trace) {
      const pending = this.normalizeTraceStationIds(trace && trace.pendingStationIds);
      const currentStationId = this.parseStationTaskNo(trace && trace.currentStationId);
      if (pending.length === 0) {
        return currentStationId > 0 ? [currentStationId] : [];
      }
      if (currentStationId > 0 && pending[0] !== currentStationId) {
        return [currentStationId].concat(pending);
      }
      return pending;
    },
    renderTraceOverlay() {
      if (!this.traceOverlayContainer) { return; }
      this.clearTraceOverlay();
      const trace = this.normalizeTraceOverlay(this.traceOverlay);
      if (!trace || !this.pixiStaMap || this.pixiStaMap.size === 0) { return; }
      const graphics = new PIXI.Graphics();
      this.drawTracePairs(graphics, trace.passedStationIds, 0x2563eb, 5, 0.95);
      this.drawTracePairs(graphics, this.buildPendingTraceSequence(trace), 0xf97316, 4, 0.9);
      this.drawTracePairs(graphics, trace.latestAppendedPath, 0xfacc15, 7, 0.88);
      this.traceOverlayContainer.addChild(graphics);
      const currentMarker = this.drawTraceMarker(this.traceOverlayContainer, trace.currentStationId, {
        color: 0x2563eb,
        radius: 20
      });
      if (currentMarker && window.gsap) {
        this.tracePulseTween = window.gsap.to(currentMarker.scale, {
          x: 1.18,
          y: 1.18,
          duration: 0.55,
          repeat: -1,
          yoyo: true,
          ease: 'sine.inOut'
        });
      }
      if (trace.blockedStationId > 0) {
        const blockedMarker = this.drawTraceMarker(this.traceOverlayContainer, trace.blockedStationId, {
          color: 0xdc2626,
          radius: 22
        });
        if (blockedMarker) {
          blockedMarker.alpha = 0.95;
        }
      }
    },
    handleLoopCardEnter(loopItem) {
      if (!loopItem) { return; }
      this.hoverLoopNo = loopItem.loopNo;
@@ -2657,9 +2794,6 @@
    }
  }
});