Bläddra i källkod

feat: support for mobile

chenlei 1 vecka sedan
förälder
incheckning
460374577d

+ 93 - 1
packages/pc/index.html

@@ -5,9 +5,101 @@
     <link rel="icon" href="/favicon.ico" />
     <meta name="viewport" content="width=device-width, initial-scale=1.0" />
     <title>镇水安澜</title>
+    <style>
+      * {
+        margin: 0;
+        padding: 0;
+        box-sizing: border-box;
+      }
+
+      #app-container {
+        width: 100vw;
+        height: 100vh;
+        overflow-y: auto;
+      }
+
+      #app {
+        width: 100%;
+        height: 100%;
+      }
+
+      @media (max-width: 768px) and (orientation: portrait) {
+        body:not(.in-iframe) #app-container {
+          position: fixed;
+          top: 50%;
+          left: 50%;
+          width: 100vh;
+          height: 100vw;
+          transform: translate(-50%, -50%) rotate(90deg);
+          transform-origin: center center;
+        }
+
+        body:not(.in-iframe) #app {
+          display: none;
+        }
+
+        body:not(.in-iframe) #app-iframe {
+          display: block;
+          width: 100%;
+          height: 100%;
+          border: none;
+        }
+      }
+
+      body.in-iframe #app-container {
+        width: 100%;
+        height: 100%;
+      }
+
+      body.in-iframe #app-iframe {
+        display: none;
+      }
+
+      #app-iframe {
+        display: none;
+      }
+    </style>
   </head>
   <body>
-    <div id="app"></div>
+    <div id="app-container">
+      <div id="app"></div>
+      <iframe id="app-iframe"></iframe>
+    </div>
+    <script>
+      if (window.self !== window.top) {
+        document.body.classList.add("in-iframe");
+      }
+
+      if (window.self === window.top) {
+        function handleOrientation() {
+          const app = document.getElementById("app");
+          const iframe = document.getElementById("app-iframe");
+
+          if (
+            window.innerWidth <= 768 &&
+            window.innerHeight > window.innerWidth
+          ) {
+            app.style.display = "none";
+            iframe.style.display = "block";
+            if (!iframe.src) {
+              const url = new URL(window.location.href);
+              url.searchParams.set("iframe", "1");
+              iframe.src = url.toString();
+            }
+          } else {
+            app.style.display = "block";
+            iframe.style.display = "none";
+          }
+        }
+
+        handleOrientation();
+
+        window.addEventListener("resize", handleOrientation);
+        window.addEventListener("orientationchange", () => {
+          setTimeout(handleOrientation, 100);
+        });
+      }
+    </script>
     <script type="module" src="/src/main.js"></script>
   </body>
 </html>

+ 16 - 13
packages/pc/src/views/Home/map.scss

@@ -1,3 +1,5 @@
+@use "@/assets/utils.scss";
+
 .home-map {
   position: relative;
   padding-top: 30px;
@@ -92,6 +94,9 @@
       transition: none;
       will-change: transform;
     }
+    img {
+      width: 2752px;
+    }
     &-point {
       position: absolute;
       top: 0;
@@ -99,26 +104,24 @@
       display: flex;
       color: #604424;
 
-      // prettier-ignore
       &-label {
         position: relative;
-        top: 40PX;
-        width: 102PX;
-        height: 170PX;
-        font-size: 27PX;
-        line-height: 102PX;
+        top: 40px;
+        width: 102px;
+        height: 170px;
+        font-size: 27px;
+        line-height: 102px;
         text-align: center;
         writing-mode: vertical-lr;
-        letter-spacing: 10PX;
+        letter-spacing: 10px;
         background: url("./images/label-bg-min.png") no-repeat center / contain;
       }
-      // prettier-ignore
       &-content {
-        padding: 20PX 85PX 20PX 25PX;
-        width: 440PX;
-        height: 142PX;
-        font-size: 16PX;
-        line-height: 26PX;
+        padding: 20px 85px 20px 25px;
+        width: 440px;
+        height: 142px;
+        font-size: 16px;
+        line-height: 26px;
         background: url("./images/card-bg-min.png") no-repeat center / contain;
       }
     }

+ 61 - 10
packages/pc/src/views/Home/map.vue

@@ -41,7 +41,7 @@
 </template>
 
 <script setup>
-import { ref, computed, onUnmounted, watch, nextTick } from "vue";
+import { ref, computed, onUnmounted, watch, nextTick, onMounted } from "vue";
 import { IRON_BULL_LIST } from "@canal/base";
 
 const mapContainer = ref(null);
@@ -53,7 +53,10 @@ const currentX = ref(0);
 const currentY = ref(0);
 const activeIndex = ref(-1);
 const enableTransition = ref(false);
-const POSITIONS = {
+const ORIGINAL_IMAGE_WIDTH = 2752; // 原始图片宽度(px)
+
+// 原始坐标(基于 2752px 宽度)
+const ORIGINAL_POSITIONS = {
   0: {
     x: -17,
     y: 380,
@@ -76,6 +79,24 @@ const POSITIONS = {
   },
 };
 
+// 根据图片实际渲染尺寸动态计算坐标
+const POSITIONS = computed(() => {
+  if (!mapImage.value) return ORIGINAL_POSITIONS;
+
+  const actualWidth = mapImage.value.getBoundingClientRect().width;
+  const scale = actualWidth / ORIGINAL_IMAGE_WIDTH;
+
+  const positions = {};
+  Object.keys(ORIGINAL_POSITIONS).forEach((key) => {
+    positions[key] = {
+      x: ORIGINAL_POSITIONS[key].x * scale,
+      y: ORIGINAL_POSITIONS[key].y * scale,
+    };
+  });
+
+  return positions;
+});
+
 const imageStyle = computed(() => {
   return {
     transform: `translate(${currentX.value}px, ${currentY.value}px)`,
@@ -96,6 +117,21 @@ const handleMouseDown = (e) => {
   document.addEventListener("mouseup", handleMouseUp);
 };
 
+const DESIGN_VIEWPORT_WIDTH = 1920;
+const MAP_OFFSET_X_RATIO = 395 / DESIGN_VIEWPORT_WIDTH;
+const MAP_OFFSET_Y_RATIO = 30 / DESIGN_VIEWPORT_WIDTH;
+
+const getMapOffsets = () => {
+  const fallback = { offsetX: 395, offsetY: 30 };
+  if (!mapContainer.value) return fallback;
+
+  const { width } = mapContainer.value.getBoundingClientRect();
+  return {
+    offsetX: width * MAP_OFFSET_X_RATIO,
+    offsetY: width * MAP_OFFSET_Y_RATIO,
+  };
+};
+
 const handleMouseMove = (e) => {
   if (!isDragging.value) return;
 
@@ -107,15 +143,17 @@ const handleMouseMove = (e) => {
     const containerRect = mapContainer.value.getBoundingClientRect();
     const imageRect = mapImage.value.getBoundingClientRect();
 
+    const { offsetX, offsetY } = getMapOffsets();
+
     // 获取图片的实际尺寸(考虑缩放)
-    const imageWidth = imageRect.width + 395;
-    const imageHeight = imageRect.height + 30;
+    const imageWidthValue = imageRect.width + offsetX;
+    const imageHeight = imageRect.height + offsetY;
     const containerWidth = containerRect.width;
     const containerHeight = containerRect.height;
 
     // 计算允许的最大和最小偏移量
     const maxX = 0;
-    const minX = containerWidth - imageWidth;
+    const minX = containerWidth - imageWidthValue;
     const maxY = 0;
     const minY = containerHeight - imageHeight;
 
@@ -140,8 +178,10 @@ const centerOnPoint = (index) => {
     const containerRect = mapContainer.value.getBoundingClientRect();
     const imageRect = mapImage.value.getBoundingClientRect();
 
-    const imageWidth = imageRect.width + 395;
-    const imageHeight = imageRect.height + 30;
+    const { offsetX, offsetY } = getMapOffsets();
+
+    const imageWidthValue = imageRect.width + offsetX;
+    const imageHeight = imageRect.height + offsetY;
     const containerWidth = containerRect.width;
     const containerHeight = containerRect.height;
 
@@ -150,12 +190,12 @@ const centerOnPoint = (index) => {
     const centerY = containerHeight / 2;
 
     // 计算目标位置:让点位于容器中心
-    const targetX = centerX - POSITIONS[index].x - 395;
-    const targetY = centerY - POSITIONS[index].y;
+    const targetX = centerX - POSITIONS.value[index].x - offsetX;
+    const targetY = centerY - POSITIONS.value[index].y;
 
     // 计算允许的最大和最小偏移量
     const maxX = 0;
-    const minX = containerWidth - imageWidth;
+    const minX = containerWidth - imageWidthValue;
     const maxY = 0;
     const minY = containerHeight - imageHeight;
 
@@ -171,15 +211,26 @@ const centerOnPoint = (index) => {
   });
 };
 
+const handleResize = () => {
+  if (activeIndex.value !== -1) {
+    centerOnPoint(activeIndex.value);
+  }
+};
+
 watch(activeIndex, (newIndex) => {
   if (newIndex !== -1) {
     centerOnPoint(newIndex);
   }
 });
 
+onMounted(() => {
+  window.addEventListener("resize", handleResize);
+});
+
 onUnmounted(() => {
   document.removeEventListener("mousemove", handleMouseMove);
   document.removeEventListener("mouseup", handleMouseUp);
+  window.removeEventListener("resize", handleResize);
 });
 </script>
 

+ 0 - 1
packages/pc/src/views/Step2/detail.vue

@@ -28,7 +28,6 @@
         <swiper-slide v-for="(item, index) in detail.imgs" :key="item.thumb">
           <el-image
             :src="baseUrl + item.thumb"
-            lazy
             fit="cover"
             preview-teleported
             :preview-src-list="detail.imgs.map((item) => baseUrl + item.img)"

+ 3 - 0
packages/pc/vite.config.js

@@ -10,6 +10,9 @@ import { ElementPlusResolver } from "unplugin-vue-components/resolvers";
 // https://vite.dev/config/
 export default defineConfig({
   base: "./",
+  server: {
+    host: "0.0.0.0",
+  },
   plugins: [
     vue(),
     AutoImport({