From 43a94fb2dbd0028a1c4fed5bbda6cb13b0365d60 Mon Sep 17 00:00:00 2001
From: Junjie <DELL@qq.com>
Date: 星期三, 04 二月 2026 08:50:13 +0800
Subject: [PATCH] #

---
 src/main/webapp/components/MapCanvas.js |  235 +++++++++++++++++++++++++++++++++++++++++++++++++---------
 src/main/resources/application.yml      |    2 
 2 files changed, 197 insertions(+), 40 deletions(-)

diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml
index e810673..992768a 100644
--- a/src/main/resources/application.yml
+++ b/src/main/resources/application.yml
@@ -72,7 +72,7 @@
   threadControlCount: 10
   liftType: lift
 
-mainProcessPlugin: XiaosongProcess
+mainProcessPlugin: FakeProcess
 
 deviceLogStorage:
   # 璁惧鏃ュ織瀛樺偍鏂瑰紡 mysql file
diff --git a/src/main/webapp/components/MapCanvas.js b/src/main/webapp/components/MapCanvas.js
index 598a962..c87a755 100644
--- a/src/main/webapp/components/MapCanvas.js
+++ b/src/main/webapp/components/MapCanvas.js
@@ -2,8 +2,12 @@
   template: `
     <div style="width: 100%; height: 100%; position: relative;">
       <div ref="pixiView"></div>
-      <div style="position: absolute; top: 20px; right: 50px;">
+      <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="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>
       </div>
     </div>
   `,
@@ -24,6 +28,14 @@
       pixiCrnMap: new Map(),
       pixiDualCrnMap: new Map(),
       pixiRgvMap: new Map(),
+      mapRoot: null,
+      mapRotation: 0,
+      mapMirrorX: false,
+      mapContentSize: { width: 0, height: 0 },
+      mapConfigCodes: {
+        rotate: 'map_canvas_rotation',
+        mirror: 'map_canvas_mirror_x'
+      },
       pixiShelfMap: new Map(),
       pixiTrackMap: new Map(),
       pixiDevpTextureMap: new Map(),
@@ -51,6 +63,7 @@
     mounted() {
     this.currentLev = this.lev || 1;
     this.createMap();
+    this.loadMapTransformConfig();
     this.connectWs();
     
     setTimeout(() => {
@@ -137,11 +150,13 @@
       this.shelvesContainer = new PIXI.ParticleContainer(10000, { scale: true, position: true, rotation: false, uvs: false, alpha: false });
       this.tracksContainer.autoResize = true;
       this.shelvesContainer.autoResize = true;
-      this.pixiApp.stage.addChild(this.tracksGraphics);
-      this.pixiApp.stage.addChild(this.tracksContainer);
-      this.pixiApp.stage.addChild(this.shelvesContainer);
-      this.pixiApp.stage.addChild(this.objectsContainer);
-      this.pixiApp.stage.addChild(this.objectsContainer2);
+      this.mapRoot = new PIXI.Container();
+      this.pixiApp.stage.addChild(this.mapRoot);
+      this.mapRoot.addChild(this.tracksGraphics);
+      this.mapRoot.addChild(this.tracksContainer);
+      this.mapRoot.addChild(this.shelvesContainer);
+      this.mapRoot.addChild(this.objectsContainer);
+      this.mapRoot.addChild(this.objectsContainer2);
       this.pixiApp.renderer.roundPixels = true;
       
 
@@ -173,14 +188,19 @@
         const rect = this.pixiApp.view.getBoundingClientRect();
         const sx = event.clientX - rect.left;
         const sy = event.clientY - rect.top;
-        const oldZoom = this.pixiApp.stage.scale.x;
+        const oldZoomX = this.pixiApp.stage.scale.x || 1;
+        const oldZoomY = this.pixiApp.stage.scale.y || 1;
+        const oldZoomAbs = Math.abs(oldZoomX) || 1;
         const delta = event.deltaY;
-        let newZoom = oldZoom * 0.999 ** delta;
-        const worldX = (sx - this.pixiApp.stage.position.x) / oldZoom;
-        const worldY = (sy - this.pixiApp.stage.position.y) / oldZoom;
-        const newPosX = sx - worldX * newZoom;
-        const newPosY = sy - worldY * newZoom;
-        this.pixiApp.stage.setTransform(newPosX, newPosY, newZoom, newZoom, 0, 0, 0, 0, 0);
+        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 newPosX = sx - worldX * newZoomX;
+        const newPosY = sy - worldY * newZoomY;
+        this.pixiApp.stage.setTransform(newPosX, newPosY, newZoomX, newZoomY, 0, 0, 0, 0, 0);
           this.scheduleAdjustLabels();
       });
       //*******************缂╂斁鐢诲竷*******************
@@ -483,14 +503,8 @@
           if (bottom > contentH) { contentH = bottom; }
         }
       }
-      const vw = this.pixiApp.view.width;
-      const vh = this.pixiApp.view.height;
-      let scale = Math.min(vw / contentW, vh / contentH) * 0.95;
-      if (!isFinite(scale) || scale <= 0) { scale = 1; }
-      const posX = (vw - contentW * scale) / 2;
-      const posY = (vh - contentH * scale) / 2;
-      this.pixiApp.stage.setTransform(posX, posY, scale, scale, 0, 0, 0, 0, 0);
-      this.adjustLabelScale();
+      this.mapContentSize = { width: contentW, height: contentH };
+      this.applyMapTransform(true);
       this.map = map;
       this.isSwitchingFloor = false;
     },
@@ -1312,12 +1326,14 @@
       if (this.isJson(obj)) { let data = JSON.parse(obj); if (data.trackSiteNo == null || data.trackSiteNo == undefined) { return -1; } return data.trackSiteNo; } else { return -1; }
     },
     adjustLabelScale() {
-      const s = this.pixiApp && this.pixiApp.stage ? (this.pixiApp.stage.scale.x || 1) : 1;
+      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 pos = this.pixiApp.stage.position;
       const margin = 50;
+      const mirrorSign = this.mapMirrorX ? -1 : 1;
+      const inverseRotation = -((this.mapRotation % 360) * Math.PI / 180);
+      const tmpPoint = new PIXI.Point();
       this.pixiStaMap && this.pixiStaMap.forEach((sprite) => {
         const textObj = sprite && sprite.textObj;
         if (!textObj) { return; }
@@ -1325,11 +1341,11 @@
         let scale = minPx / (base * s);
         if (!isFinite(scale)) { scale = 1; }
         scale = Math.max(0.8, Math.min(scale, 3));
-        textObj.scale.set(scale);
+        textObj.scale.set(scale * mirrorSign, scale);
+        textObj.rotation = inverseRotation;
         textObj.position.set(sprite.width / 2, sprite.height / 2);
-        const sx = pos.x + sprite.x * s;
-        const sy = pos.y + sprite.y * s;
-        const on = sx >= -margin && sy >= -margin && sx <= vw + margin && sy <= vh + margin;
+        sprite.getGlobalPosition(tmpPoint);
+        const on = tmpPoint.x >= -margin && tmpPoint.y >= -margin && tmpPoint.x <= vw + margin && tmpPoint.y <= vh + margin;
         textObj.visible = (s >= 0.25) && on;
       });
       this.pixiCrnMap && this.pixiCrnMap.forEach((sprite) => {
@@ -1339,11 +1355,11 @@
         let scale = minPx / (base * s);
         if (!isFinite(scale)) { scale = 1; }
         scale = Math.max(0.8, Math.min(scale, 3));
-        textObj.scale.set(scale);
+        textObj.scale.set(scale * mirrorSign, scale);
+        textObj.rotation = inverseRotation;
         textObj.position.set(sprite.width / 2, sprite.height / 2);
-        const sx = pos.x + sprite.x * s;
-        const sy = pos.y + sprite.y * s;
-        const on = sx >= -margin && sy >= -margin && sx <= vw + margin && sy <= vh + margin;
+        sprite.getGlobalPosition(tmpPoint);
+        const on = tmpPoint.x >= -margin && tmpPoint.y >= -margin && tmpPoint.x <= vw + margin && tmpPoint.y <= vh + margin;
         textObj.visible = (s >= 0.25) && on;
       });
       this.pixiDualCrnMap && this.pixiDualCrnMap.forEach((sprite) => {
@@ -1353,11 +1369,11 @@
         let scale = minPx / (base * s);
         if (!isFinite(scale)) { scale = 1; }
         scale = Math.max(0.8, Math.min(scale, 3));
-        textObj.scale.set(scale);
+        textObj.scale.set(scale * mirrorSign, scale);
+        textObj.rotation = inverseRotation;
         textObj.position.set(sprite.width / 2, sprite.height / 2);
-        const sx = pos.x + sprite.x * s;
-        const sy = pos.y + sprite.y * s;
-        const on = sx >= -margin && sy >= -margin && sx <= vw + margin && sy <= vh + margin;
+        sprite.getGlobalPosition(tmpPoint);
+        const on = tmpPoint.x >= -margin && tmpPoint.y >= -margin && tmpPoint.x <= vw + margin && tmpPoint.y <= vh + margin;
         textObj.visible = (s >= 0.25) && on;
       });
       this.pixiRgvMap && this.pixiRgvMap.forEach((sprite) => {
@@ -1367,14 +1383,155 @@
         let scale = minPx / (base * s);
         if (!isFinite(scale)) { scale = 1; }
         scale = Math.max(0.8, Math.min(scale, 3));
-        textObj.scale.set(scale);
+        textObj.scale.set(scale * mirrorSign, scale);
+        textObj.rotation = inverseRotation;
         textObj.position.set(sprite.width / 2, sprite.height / 2);
-        const sx = pos.x + sprite.x * s;
-        const sy = pos.y * s + pos.y;
-        const on = sx >= -margin && sy >= -margin && sx <= vw + margin && sy <= vh + margin;
+        sprite.getGlobalPosition(tmpPoint);
+        const on = tmpPoint.x >= -margin && tmpPoint.y >= -margin && tmpPoint.x <= vw + margin && tmpPoint.y <= vh + margin;
         textObj.visible = (s >= 0.25) && on;
       });
     },
+    rotateMap() {
+      this.mapRotation = (this.mapRotation + 90) % 360;
+      this.applyMapTransform(true);
+      this.saveMapTransformConfig();
+    },
+    toggleMirror() {
+      this.mapMirrorX = !this.mapMirrorX;
+      this.applyMapTransform(true);
+      this.saveMapTransformConfig();
+    },
+    parseRotation(value) {
+      const num = parseInt(value, 10);
+      if (!isFinite(num)) { return 0; }
+      const rot = ((num % 360) + 360) % 360;
+      return (rot === 90 || rot === 180 || rot === 270) ? rot : 0;
+    },
+    parseMirror(value) {
+      if (value === true || value === false) { return value; }
+      if (value == null) { return false; }
+      const str = String(value).toLowerCase();
+      return str === '1' || str === 'true' || str === 'y';
+    },
+    loadMapTransformConfig() {
+      if (!window.$ || typeof baseUrl === 'undefined') { return; }
+      $.ajax({
+        url: baseUrl + "/config/listAll/auth",
+        headers: { 'token': localStorage.getItem('token') },
+        dataType: 'json',
+        method: 'GET',
+        success: (res) => {
+          if (!res || res.code !== 200 || !Array.isArray(res.data)) {
+            if (res && res.code === 403) { parent.location.href = baseUrl + "/login"; }
+            return;
+          }
+          const byCode = {};
+          res.data.forEach((item) => {
+            if (item && item.code) { byCode[item.code] = item; }
+          });
+          const rotateCfg = byCode[this.mapConfigCodes.rotate];
+          const mirrorCfg = byCode[this.mapConfigCodes.mirror];
+          if (rotateCfg && rotateCfg.value != null) {
+            this.mapRotation = this.parseRotation(rotateCfg.value);
+          }
+          if (mirrorCfg && mirrorCfg.value != null) {
+            this.mapMirrorX = this.parseMirror(mirrorCfg.value);
+          }
+          if (rotateCfg == null || mirrorCfg == null) {
+            this.createMapTransformConfigIfMissing(rotateCfg, mirrorCfg);
+          }
+          if (this.mapContentSize && this.mapContentSize.width > 0) {
+            this.applyMapTransform(true);
+          }
+        }
+      });
+    },
+    createMapTransformConfigIfMissing(rotateCfg, mirrorCfg) {
+      if (!window.$ || typeof baseUrl === 'undefined') { return; }
+      const createList = [];
+      if (!rotateCfg) {
+        createList.push({
+          name: '鍦板浘鏃嬭浆',
+          code: this.mapConfigCodes.rotate,
+          value: String(this.mapRotation || 0),
+          type: 1,
+          status: 1,
+          selectType: 'map'
+        });
+      }
+      if (!mirrorCfg) {
+        createList.push({
+          name: '鍦板浘闀滃儚',
+          code: this.mapConfigCodes.mirror,
+          value: this.mapMirrorX ? '1' : '0',
+          type: 1,
+          status: 1,
+          selectType: 'map'
+        });
+      }
+      createList.forEach((cfg) => {
+        $.ajax({
+          url: baseUrl + "/config/add/auth",
+          headers: { 'token': localStorage.getItem('token') },
+          method: 'POST',
+          data: cfg
+        });
+      });
+    },
+    saveMapTransformConfig() {
+      if (!window.$ || typeof baseUrl === 'undefined') { return; }
+      const updateList = [
+        { code: this.mapConfigCodes.rotate, value: String(this.mapRotation || 0) },
+        { code: this.mapConfigCodes.mirror, value: this.mapMirrorX ? '1' : '0' }
+      ];
+      $.ajax({
+        url: baseUrl + "/config/updateBatch",
+        headers: { 'token': localStorage.getItem('token') },
+        data: JSON.stringify(updateList),
+        dataType: 'json',
+        contentType: 'application/json;charset=UTF-8',
+        method: 'POST'
+      });
+    },
+    getTransformedContentSize() {
+      const size = this.mapContentSize || { width: 0, height: 0 };
+      const w = size.width || 0;
+      const h = size.height || 0;
+      const rot = ((this.mapRotation % 360) + 360) % 360;
+      const swap = rot === 90 || rot === 270;
+      return { width: swap ? h : w, height: swap ? w : h };
+    },
+    fitStageToContent() {
+      if (!this.pixiApp || !this.mapContentSize) { return; }
+      const size = this.getTransformedContentSize();
+      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;
+      let scale = Math.min(vw / contentW, vh / contentH) * 0.95;
+      if (!isFinite(scale) || scale <= 0) { scale = 1; }
+      const baseW = this.mapContentSize.width || contentW;
+      const baseH = this.mapContentSize.height || contentH;
+      const mirrorX = this.mapMirrorX ? -1 : 1;
+      const scaleX = scale * mirrorX;
+      const scaleY = scale;
+      const posX = (vw / 2) - (baseW / 2) * scaleX;
+      const posY = (vh / 2) - (baseH / 2) * scaleY;
+      this.pixiApp.stage.setTransform(posX, posY, scaleX, scaleY, 0, 0, 0, 0, 0);
+    },
+    applyMapTransform(fitToView) {
+      if (!this.mapRoot || !this.mapContentSize) { return; }
+      const contentW = this.mapContentSize.width || 0;
+      const contentH = this.mapContentSize.height || 0;
+      if (contentW <= 0 || contentH <= 0) { return; }
+      this.mapRoot.pivot.set(contentW / 2, contentH / 2);
+      this.mapRoot.position.set(contentW / 2, contentH / 2);
+      this.mapRoot.rotation = (this.mapRotation % 360) * Math.PI / 180;
+      this.mapRoot.scale.set(1, 1);
+      if (fitToView) { this.fitStageToContent(); }
+      this.scheduleAdjustLabels();
+    },
     scheduleAdjustLabels() {
       if (this.adjustLabelTimer) { clearTimeout(this.adjustLabelTimer); }
       this.adjustLabelTimer = setTimeout(() => {

--
Gitblit v1.9.1