Junjie
2 天以前 63b01db83d9aad8a15276b4236a9a22e4aeef065
src/main/webapp/components/MapCanvas.js
@@ -1,8 +1,8 @@
Vue.component('map-canvas', {
const mapCanvasDefinition = {
  template: `
    <div style="width: 100%; height: 100%; position: relative;">
      <div ref="pixiView" style="position: absolute; inset: 0;"></div>
      <div :style="cycleCapacityPanelStyle()">
      <div v-if="cycleCapacityVisible !== false" :style="cycleCapacityPanelStyle()">
        <div style="display: flex; flex-direction: column; gap: 6px; align-items: flex-start;">
          <div v-for="item in cycleCapacity.loopList"
               :key="'loop-' + item.loopNo"
@@ -21,7 +21,7 @@
           :style="shelfTooltipStyle()">
        {{ shelfTooltip.text }}
      </div>
      <div style="position: absolute; top: 18px; right: 34px; z-index: 30; display: flex; flex-direction: column; align-items: flex-end; gap: 8px;">
      <div v-if="toolPanelEnabled !== false" style="position: absolute; top: 18px; right: 34px; z-index: 30; display: flex; flex-direction: column; align-items: flex-end; gap: 8px;">
        <div :style="mapToolFpsStyle()">FPS {{ mapFps }}</div>
        <button type="button" @click="toggleMapToolPanel" :style="mapToolToggleStyle(showMapToolPanel)">{{ showMapToolPanel ? '收起操作' : '地图操作' }}</button>
        <div v-show="showMapToolPanel" :style="mapToolBarStyle()">
@@ -33,6 +33,7 @@
          </div>
          <div :style="mapToolRowStyle()">
            <button type="button" @click="openStationColorConfigPage" :style="mapToolButtonStyle(false)">站点颜色</button>
            <button v-if="fakeOperationVisible" type="button" @click="openFakeOperationConfigPage" :style="mapToolButtonStyle(false)">仿真操作</button>
          </div>
          <div v-if="levList && levList.length > 1" :style="mapToolFloorSectionStyle()">
            <div :style="mapToolSectionLabelStyle()">楼层</div>
@@ -50,7 +51,7 @@
      </div>
    </div>
  `,
  props: ['lev', 'levList', 'crnParam', 'rgvParam', 'devpParam', 'stationTaskRange', 'highlightOnParamChange', 'viewportPadding', 'hudPadding', 'traceOverlay'],
  props: ['lev', 'levList', 'crnParam', 'rgvParam', 'devpParam', 'stationTaskRange', 'highlightOnParamChange', 'viewportPadding', 'hudPadding', 'traceOverlay', 'realtimeEnabled', 'toolPanelEnabled', 'cycleCapacityVisible'],
  data() {
    return {
      map: [],
@@ -77,6 +78,8 @@
      },
      pixiShelfMap: new Map(),
      pixiTrackMap: new Map(),
      pixiCrnTextureMap: new Map(),
      pixiRgvTextureMap: new Map(),
      pixiDevpTextureMap: new Map(),
      pixiCrnColorTextureMap: new Map(),
      pixiDevpTextureMap: new Map(),
@@ -149,8 +152,14 @@
        'site-unauto': 0xb8b8b8,
        'machine-pakin': 0x30bffc,
        'machine-pakout': 0x97b400,
        'site-run-block': 0xe69138
      }
        'site-run-block': 0xe69138,
        'site-error': 0xDB2828
      },
      fakeOperationVisible: false,
      runtimeCleanupFns: [],
      ownsPixiApp: true,
      pixiStageHost: null,
      pixiStageTransformTarget: null
    }
  },
    mounted() {
@@ -160,19 +169,26 @@
    this.loadMapTransformConfig();
    this.loadStationColorConfig();
    this.loadLocList();
    this.connectWs();
    if (this.realtimeEnabled !== false) {
      this.loadFakeProcessStatus();
      this.connectWs();
      setTimeout(() => {
        this.getMap(this.currentLev);
      }, 1000);
      this.timer = setInterval(() => {
        this.getCrnInfo();
        this.getDualCrnInfo();
        this.getSiteInfo();
        this.getCycleCapacityInfo();
        this.getRgvInfo();
      }, 1000);
      return;
    }
    setTimeout(() => {
      this.getMap(this.currentLev);
    }, 1000);
    this.timer = setInterval(() => {
      this.getCrnInfo();
      this.getDualCrnInfo();
      this.getSiteInfo();
      this.getCycleCapacityInfo();
      this.getRgvInfo();
    }, 1000);
    }, 60);
  },
  beforeDestroy() {
    if (this.timer) { clearInterval(this.timer); }
@@ -180,9 +196,20 @@
    if (this.hoverRaf) { cancelAnimationFrame(this.hoverRaf); this.hoverRaf = null; }
    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); }
    const stageTransformTarget = this.getStageTransformTarget();
    if (window.gsap && stageTransformTarget && stageTransformTarget.position) { window.gsap.killTweensOf(stageTransformTarget.position); }
    this.clearTraceOverlay();
    if (this.pixiApp) { this.pixiApp.destroy(true, { children: true }); }
    while (this.runtimeCleanupFns && this.runtimeCleanupFns.length) {
      const cleanup = this.runtimeCleanupFns.pop();
      try {
        cleanup();
      } catch (error) {}
    }
    if (this.pixiApp && this.ownsPixiApp !== false) {
      this.pixiApp.destroy(true, { children: true });
    } else if (this.mapRoot && this.mapRoot.parent) {
      this.mapRoot.parent.removeChild(this.mapRoot);
    }
    if (this.containerResizeObserver) { this.containerResizeObserver.disconnect(); this.containerResizeObserver = null; }
    window.removeEventListener('resize', this.scheduleResizeToContainer);
    if (this.wsReconnectTimer) { clearTimeout(this.wsReconnectTimer); this.wsReconnectTimer = null; }
@@ -384,12 +411,22 @@
      this.$emit('switch-lev', lev);
    },
    createMap() {
      this.pixiApp = new PIXI.Application({ backgroundColor: 0xF5F7F9, antialias: false, powerPreference: 'high-performance', autoDensity: true, resolution: Math.min(window.devicePixelRatio || 1, 2) });
      const usesExternalApp = !!this.pixiApp;
      if (!this.pixiApp) {
        this.pixiApp = new PIXI.Application({ backgroundColor: 0xF5F7F9, antialias: false, powerPreference: 'high-performance', autoDensity: true, resolution: Math.min(window.devicePixelRatio || 1, 2) });
        this.ownsPixiApp = this.ownsPixiApp !== false;
      } else {
        this.ownsPixiApp = false;
      }
      PIXI.settings.SCALE_MODE = PIXI.SCALE_MODES.LINEAR;
      this.$refs.pixiView.appendChild(this.pixiApp.view);
      this.pixiApp.view.style.width = '100%';
      this.pixiApp.view.style.height = '100%';
      this.pixiApp.view.style.display = 'block';
      if (!usesExternalApp && this.$refs.pixiView && this.pixiApp.view && this.pixiApp.view.parentNode !== this.$refs.pixiView) {
        this.$refs.pixiView.appendChild(this.pixiApp.view);
      }
      if (this.pixiApp.view) {
        this.pixiApp.view.style.width = '100%';
        this.pixiApp.view.style.height = '100%';
        this.pixiApp.view.style.display = 'block';
      }
      this.resizeToContainer();
      window.addEventListener('resize', this.scheduleResizeToContainer);
      this.graphicsCrnTrack = this.createTrackTexture(25, 25, 10);
@@ -402,7 +439,10 @@
      this.shelvesContainer = new PIXI.Container();
      this.tracksContainer.autoResize = true;
      this.mapRoot = new PIXI.Container();
      this.pixiApp.stage.addChild(this.mapRoot);
      const stageHostContainer = this.getStageHostContainer();
      if (stageHostContainer) {
        stageHostContainer.addChild(this.mapRoot);
      }
      this.mapRoot.addChild(this.tracksGraphics);
      this.mapRoot.addChild(this.tracksContainer);
      this.mapRoot.addChild(this.shelvesContainer);
@@ -417,7 +457,8 @@
      //*******************shelf hover*******************
      this.pixiApp.renderer.plugins.interaction.on('pointermove', (event) => {
      const interaction = this.pixiApp.renderer.plugins.interaction;
      const hoverMoveHandler = (event) => {
        if (!this.isShelfTooltipAllowed()) { this.hideShelfTooltip(); return; }
        if (!this.map || !this.mapRoot) { return; }
        const pos = event.data.global;
@@ -428,55 +469,84 @@
          this.hoverRaf = null;
          this.updateShelfHoverFromPointer(this.hoverPointer);
        });
      };
      interaction.on('pointermove', hoverMoveHandler);
      this.pushRuntimeCleanup(() => {
        interaction.off('pointermove', hoverMoveHandler);
      });
      this.pixiApp.view.addEventListener('mouseleave', () => {
      const mouseLeaveHandler = () => {
        this.hoveredShelfCell = null;
        this.hideShelfTooltip();
      };
      this.pixiApp.view.addEventListener('mouseleave', mouseLeaveHandler);
      this.pushRuntimeCleanup(() => {
        if (this.pixiApp && this.pixiApp.view) {
          this.pixiApp.view.removeEventListener('mouseleave', mouseLeaveHandler);
        }
      });
      //*******************shelf hover*******************
      let stageOriginalPos;
      let mouseDownPoint;
      let touchBlank = false;
      this.pixiApp.renderer.plugins.interaction.on('pointerdown', (event) => {
      const pointerDownHandler = (event) => {
        const globalPos = event.data.global;
        stageOriginalPos = [this.pixiApp.stage.position.x, this.pixiApp.stage.position.y];
        const stageTransformTarget = this.getStageTransformTarget();
        stageOriginalPos = stageTransformTarget ? [stageTransformTarget.position.x, stageTransformTarget.position.y] : [0, 0];
        mouseDownPoint = [globalPos.x, globalPos.y];
        if (!event.target || (event.target && event.target._kind === 'shelf')) { touchBlank = true; }
      });
      this.pixiApp.renderer.plugins.interaction.on('pointermove', (event) => {
      };
      const pointerDragHandler = (event) => {
        const globalPos = event.data.global;
        if (touchBlank) {
          const dx = globalPos.x - mouseDownPoint[0];
          const dy = globalPos.y - mouseDownPoint[1];
          this.pixiApp.stage.position.set(stageOriginalPos[0] + dx, stageOriginalPos[1] + dy);
          const stageTransformTarget = this.getStageTransformTarget();
          if (!stageTransformTarget) { return; }
          stageTransformTarget.position.set(stageOriginalPos[0] + dx, stageOriginalPos[1] + dy);
          this.scheduleShelfChunkCulling();
        }
      };
      const pointerUpHandler = () => { touchBlank = false; };
      interaction.on('pointerdown', pointerDownHandler);
      interaction.on('pointermove', pointerDragHandler);
      interaction.on('pointerup', pointerUpHandler);
      this.pushRuntimeCleanup(() => {
        interaction.off('pointerdown', pointerDownHandler);
        interaction.off('pointermove', pointerDragHandler);
        interaction.off('pointerup', pointerUpHandler);
      });
      this.pixiApp.renderer.plugins.interaction.on('pointerup', () => { touchBlank = false; });
      //*******************缩放画布*******************
      this.pixiApp.view.addEventListener('wheel', (event) => {
      const wheelHandler = (event) => {
        event.stopPropagation();
        event.preventDefault();
        const rect = this.pixiApp.view.getBoundingClientRect();
        const sx = event.clientX - rect.left;
        const sy = event.clientY - rect.top;
        const oldZoomX = this.pixiApp.stage.scale.x || 1;
        const oldZoomY = this.pixiApp.stage.scale.y || 1;
        const stageTransformTarget = this.getStageTransformTarget();
        if (!stageTransformTarget) { return; }
        const oldZoomX = stageTransformTarget.scale.x || 1;
        const oldZoomY = stageTransformTarget.scale.y || 1;
        const oldZoomAbs = Math.abs(oldZoomX) || 1;
        const delta = event.deltaY;
        let newZoomAbs = oldZoomAbs * 0.999 ** delta;
        const mirrorX = this.mapMirrorX ? -1 : 1;
        const newZoomX = mirrorX * newZoomAbs;
        const newZoomY = newZoomAbs;
        const worldX = (sx - this.pixiApp.stage.position.x) / oldZoomX;
        const worldY = (sy - this.pixiApp.stage.position.y) / oldZoomY;
        const worldX = (sx - stageTransformTarget.position.x) / oldZoomX;
        const worldY = (sy - stageTransformTarget.position.y) / oldZoomY;
        const newPosX = sx - worldX * newZoomX;
        const newPosY = sy - worldY * newZoomY;
        this.pixiApp.stage.setTransform(newPosX, newPosY, newZoomX, newZoomY, 0, 0, 0, 0, 0);
        stageTransformTarget.setTransform(newPosX, newPosY, newZoomX, newZoomY, 0, 0, 0, 0, 0);
        this.scheduleAdjustLabels();
        this.scheduleShelfChunkCulling();
      };
      this.pixiApp.view.addEventListener('wheel', wheelHandler);
      this.pushRuntimeCleanup(() => {
        if (this.pixiApp && this.pixiApp.view) {
          this.pixiApp.view.removeEventListener('wheel', wheelHandler);
        }
      });
      //*******************缩放画布*******************
@@ -487,7 +557,7 @@
      let fpsDeltaSumMs = 0;
      let fpsFrameCount = 0;
      const fpsUpdateInterval = 200;
      this.pixiApp.ticker.add((delta) => {
      const tickerHandler = () => {
        const timeNow = (new Date()).getTime();
        const timeDiff = timeNow - g_Time;
        g_Time = timeNow;
@@ -499,6 +569,12 @@
          fpsDeltaSumMs = 0;
          fpsFrameCount = 0;
          fpsLastUpdateTs = timeNow;
        }
      };
      this.pixiApp.ticker.add(tickerHandler);
      this.pushRuntimeCleanup(() => {
        if (this.pixiApp && this.pixiApp.ticker) {
          this.pixiApp.ticker.remove(tickerHandler);
        }
      });
      //*******************FPS*******************
@@ -528,6 +604,19 @@
      const rect = this.pixiApp.view ? this.pixiApp.view.getBoundingClientRect() : null;
      return { width: rect ? rect.width : 0, height: rect ? rect.height : 0 };
    },
    getStageHostContainer() {
      if (this.pixiStageHost) { return this.pixiStageHost; }
      return this.pixiApp ? this.pixiApp.stage : null;
    },
    getStageTransformTarget() {
      const transformTarget = this.pixiStageTransformTarget || this.getStageHostContainer();
      return transformTarget || null;
    },
    pushRuntimeCleanup(cleanup) {
      if (typeof cleanup !== 'function') { return; }
      this.runtimeCleanupFns = this.runtimeCleanupFns || [];
      this.runtimeCleanupFns.push(cleanup);
    },
    getViewportPadding() {
      return this.normalizeViewportPadding(this.viewportPadding);
    },
@@ -554,7 +643,8 @@
      };
    },
    adjustStageForViewportPadding(oldPadding, newPadding) {
      if (!this.pixiApp || !this.pixiApp.stage) { return; }
      const stageTransformTarget = this.getStageTransformTarget();
      if (!this.pixiApp || !stageTransformTarget) { return; }
      const viewport = this.getViewportSize();
      if (viewport.width <= 0 || viewport.height <= 0) { return; }
      const prevCenter = this.getViewportCenter(viewport, oldPadding);
@@ -564,11 +654,11 @@
      if (Math.abs(deltaX) < 0.5 && Math.abs(deltaY) < 0.5) {
        return;
      }
      const targetX = this.pixiApp.stage.position.x + deltaX;
      const targetY = this.pixiApp.stage.position.y + deltaY;
      const targetX = stageTransformTarget.position.x + deltaX;
      const targetY = stageTransformTarget.position.y + deltaY;
      if (window.gsap) {
        window.gsap.killTweensOf(this.pixiApp.stage.position);
        window.gsap.to(this.pixiApp.stage.position, {
        window.gsap.killTweensOf(stageTransformTarget.position);
        window.gsap.to(stageTransformTarget.position, {
          x: targetX,
          y: targetY,
          duration: 0.18,
@@ -584,12 +674,18 @@
        });
        return;
      }
      this.pixiApp.stage.position.x = targetX;
      this.pixiApp.stage.position.y = targetY;
      stageTransformTarget.position.x = targetX;
      stageTransformTarget.position.y = targetY;
      this.scheduleAdjustLabels();
      this.scheduleShelfChunkCulling();
    },
    resizeToContainer() {
      if (this.ownsPixiApp === false) {
        if (this.mapContentSize && this.mapContentSize.width > 0 && this.mapContentSize.height > 0) {
          this.applyMapTransform(true);
        }
        return;
      }
      const w = this.$el.clientWidth || 0;
      const h = this.$el.clientHeight || 0;
      if (w > 0 && h > 0 && this.pixiApp) {
@@ -603,6 +699,19 @@
      }
    },
    getMap() {
      if (this.realtimeEnabled === false) {
        $.ajax({
          url: baseUrl + "/basMap/lev/" + this.currentLev + "/auth",
          headers: { token: localStorage.getItem('token') },
          method: 'GET',
          success: (res) => {
            if (res && res.code === 200) {
              this.setMap(res);
            }
          }
        });
        return;
      }
      this.sendWs(JSON.stringify({ url: "/basMap/lev/" + this.currentLev + "/auth", data: {} }));
    },
    changeFloor(lev) {
@@ -787,8 +896,7 @@
      });
      this.crnList.forEach((item) => {
        if (this.graphicsCrn == null) { this.graphicsCrn = this.createCrnTexture(item.width * 0.9, item.height * 0.9); }
        let sprite = new PIXI.Sprite(this.graphicsCrn);
        let sprite = this.createCrnSprite(item.width * 0.9, item.height * 0.9);
        const deviceNo = this.getDeviceNo(item.value);
        const taskNo = this.getTaskNo(item.value);
        const style = new PIXI.TextStyle({ fontFamily: 'Arial', fontSize: 12, fill: '#000000', stroke: '#ffffff', strokeThickness: 1 });
@@ -820,8 +928,7 @@
      });
      
      this.dualCrnList.forEach((item) => {
        if (this.graphicsCrn == null) { this.graphicsCrn = this.createCrnTexture(item.width * 0.9, item.height * 0.9); }
        let sprite = new PIXI.Sprite(this.graphicsCrn);
        let sprite = this.createCrnSprite(item.width * 0.9, item.height * 0.9);
        const deviceNo = this.getDeviceNo(item.value);
        const taskNo = this.getTaskNo(item.value);
        const style = new PIXI.TextStyle({ fontFamily: 'Arial', fontSize: 12, fill: '#000000', stroke: '#ffffff', strokeThickness: 1 });
@@ -853,8 +960,7 @@
      });
      
      this.rgvList.forEach((item) => {
        if (this.graphicsRgv == null) { this.graphicsRgv = this.createRgvTexture(item.width * 0.9, item.height * 0.9); }
        let sprite = new PIXI.Sprite(this.graphicsRgv);
        let sprite = this.createRgvSprite(item.width * 0.9, item.height * 0.9);
        const deviceNo = this.getDeviceNo(item.value);
        const taskNo = this.getTaskNo(item.value);
        const style = new PIXI.TextStyle({ fontFamily: 'Arial', fontSize: 12, fill: '#000000', stroke: '#ffffff', strokeThickness: 1 });
@@ -1158,6 +1264,15 @@
    setMap(res) {
      this.createMapData(JSON.parse(res.data));
    },
    applyReplayFrame(frame) {
      if (!frame) {
        return;
      }
      this.setSiteInfo(frame.stationStates || []);
      this.setCrnInfo(frame.crnStates || []);
      this.setDualCrnInfo(frame.dualCrnStates || []);
      this.setRgvInfo(frame.rgvStates || []);
    },
    webSocketOnOpen(e) {
      if (this.wsReconnectTimer) { clearTimeout(this.wsReconnectTimer); this.wsReconnectTimer = null; }
      this.wsReconnectAttempts = 0;
@@ -1227,6 +1342,28 @@
      if (texture == undefined) {
        texture = this.createTrackTexture(width, height, trackMask);
        this.pixiTrackMap.set(idx, texture);
      }
      return new PIXI.Sprite(texture);
    },
    createCrnSprite(width, height) {
      const w = Math.max(1, Math.round(width));
      const h = Math.max(1, Math.round(height));
      const key = w + "-" + h;
      let texture = this.pixiCrnTextureMap.get(key);
      if (texture == undefined) {
        texture = this.createCrnTexture(w, h);
        this.pixiCrnTextureMap.set(key, texture);
      }
      return new PIXI.Sprite(texture);
    },
    createRgvSprite(width, height) {
      const w = Math.max(1, Math.round(width));
      const h = Math.max(1, Math.round(height));
      const key = w + "-" + h;
      let texture = this.pixiRgvTextureMap.get(key);
      if (texture == undefined) {
        texture = this.createRgvTexture(w, h);
        this.pixiRgvTextureMap.set(key, texture);
      }
      return new PIXI.Sprite(texture);
    },
@@ -1493,6 +1630,7 @@
      return colorMap['site-unauto'] != null ? colorMap['site-unauto'] : 0xb8b8b8;
    },
    resolveStationStatus(item) {
      if (item && item.error > 0) { return 'site-error'; }
      const status = item && (item.siteStatus != null ? item.siteStatus : item.stationStatus);
      const taskNo = this.parseStationTaskNo(item && (item.workNo != null ? item.workNo : item.taskNo));
      const autoing = !!(item && item.autoing);
@@ -1517,6 +1655,9 @@
      return isNaN(taskNo) ? 0 : taskNo;
    },
    getStationTaskClass(taskNo) {
      if (window.MapCanvasShared && typeof window.MapCanvasShared.getStationTaskClass === 'function') {
        return window.MapCanvasShared.getStationTaskClass(taskNo, this.stationTaskRange);
      }
      if (!(taskNo > 0)) { return null; }
      const range = this.stationTaskRange || {};
      if (this.isTaskNoInRange(taskNo, range.inbound)) { return 'machine-pakin'; }
@@ -1524,6 +1665,9 @@
      return null;
    },
    isTaskNoInRange(taskNo, range) {
      if (window.MapCanvasShared && typeof window.MapCanvasShared.isTaskNoInRange === 'function') {
        return window.MapCanvasShared.isTaskNoInRange(taskNo, range);
      }
      if (!range) { return false; }
      const start = parseInt(range.start, 10);
      const end = parseInt(range.end, 10);
@@ -2432,8 +2576,9 @@
      return this.getStageAbsScale() >= this.shelfTooltipMinScale;
    },
    getStageAbsScale() {
      if (!this.pixiApp || !this.pixiApp.stage) { return 1; }
      return Math.abs(this.pixiApp.stage.scale.x || 1);
      const stageTransformTarget = this.getStageTransformTarget();
      if (!this.pixiApp || !stageTransformTarget) { return 1; }
      return Math.abs(stageTransformTarget.scale.x || 1);
    },
    updateShelfTooltipVisibilityByScale() {
      if (this.shelfTooltip.visible && !this.isShelfTooltipAllowed()) {
@@ -2491,7 +2636,8 @@
      };
    },
    adjustLabelScale() {
      const s = this.pixiApp && this.pixiApp.stage ? Math.abs(this.pixiApp.stage.scale.x || 1) : 1;
      const stageTransformTarget = this.getStageTransformTarget();
      const s = this.pixiApp && stageTransformTarget ? Math.abs(stageTransformTarget.scale.x || 1) : 1;
      const minPx = 14;
      const viewport = this.getViewportSize();
      const vw = viewport.width;
@@ -2593,6 +2739,40 @@
      }
      window.open(url, '_blank');
    },
    loadFakeProcessStatus() {
      if (typeof window === 'undefined' || typeof $ === 'undefined' || typeof baseUrl === 'undefined') {
        this.fakeOperationVisible = false;
        return;
      }
      $.ajax({
        url: baseUrl + '/openapi/getFakeSystemRunStatus',
        method: 'get',
        success: function (res) {
          const data = res && res.data ? res.data : null;
          this.fakeOperationVisible = !!(data && data.isFake);
        }.bind(this),
        error: function () {
          this.fakeOperationVisible = false;
        }.bind(this)
      });
    },
    openFakeOperationConfigPage() {
      if (typeof window === 'undefined' || !this.fakeOperationVisible) { return; }
      const url = (typeof baseUrl !== 'undefined' ? baseUrl : '') + '/views/watch/fakeOperationConfig.html';
      const layerInstance = (window.top && window.top.layer) || window.layer;
      if (layerInstance && typeof layerInstance.open === 'function') {
        layerInstance.open({
          type: 2,
          title: '仿真操作',
          maxmin: true,
          area: ['1180px', '820px'],
          shadeClose: false,
          content: url
        });
        return;
      }
      window.open(url, '_blank');
    },
    parseRotation(value) {
      const num = parseInt(value, 10);
      if (!isFinite(num)) { return 0; }
@@ -2615,7 +2795,8 @@
        'site-unauto': 0xb8b8b8,
        'machine-pakin': 0x30bffc,
        'machine-pakout': 0x97b400,
        'site-run-block': 0xe69138
        'site-run-block': 0xe69138,
        'site-error': 0xDB2828
      };
    },
    parseColorConfigValue(value, fallback) {
@@ -2753,6 +2934,8 @@
    },
    fitStageToContent() {
      if (!this.pixiApp || !this.mapContentSize) { return; }
      const stageTransformTarget = this.getStageTransformTarget();
      if (!stageTransformTarget) { return; }
      const size = this.getTransformedContentSize();
      const contentW = size.width || 0;
      const contentH = size.height || 0;
@@ -2774,7 +2957,7 @@
      const centerY = padding.top + availableH / 2;
      const posX = centerX - (baseW / 2) * scaleX;
      const posY = centerY - (baseH / 2) * scaleY;
      this.pixiApp.stage.setTransform(posX, posY, scaleX, scaleY, 0, 0, 0, 0, 0);
      stageTransformTarget.setTransform(posX, posY, scaleX, scaleY, 0, 0, 0, 0, 0);
    },
    applyMapTransform(fitToView) {
      if (!this.mapRoot || !this.mapContentSize) { return; }
@@ -2798,18 +2981,12 @@
      }, 20);
    }
  }
});
};
Vue.component('map-canvas', mapCanvasDefinition);
if (window.MapCanvasShared && typeof window.MapCanvasShared.registerMapRuntimeDefinition === 'function') {
  window.MapCanvasShared.registerMapRuntimeDefinition(mapCanvasDefinition);
}