dengrui 1 kuukausi sitten
vanhempi
commit
f0dd21f7a3
4 muutettua tiedostoa jossa 193 lisäystä ja 64 poistoa
  1. 1 0
      package.json
  2. 9 0
      pnpm-lock.yaml
  3. 182 63
      src/components/PDFViewFull/index.vue
  4. 1 1
      src/views/pro-steps/index.vue

+ 1 - 0
package.json

@@ -59,6 +59,7 @@
     "element-plus": "^2.9.1",
     "exceljs": "^4.4.0",
     "file-saver": "^2.0.5",
+    "hammerjs": "^2.0.8",
     "html2canvas": "^1.4.1",
     "jspdf": "^2.5.2",
     "lodash-es": "^4.17.21",

+ 9 - 0
pnpm-lock.yaml

@@ -65,6 +65,9 @@ importers:
       file-saver:
         specifier: ^2.0.5
         version: 2.0.5
+      hammerjs:
+        specifier: ^2.0.8
+        version: 2.0.8
       html2canvas:
         specifier: ^1.4.1
         version: 1.4.1
@@ -3042,6 +3045,10 @@ packages:
     resolution: {integrity: sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==}
     engines: {node: '>=10'}
 
+  hammerjs@2.0.8:
+    resolution: {integrity: sha512-tSQXBXS/MWQOn/RKckawJ61vvsDpCom87JgxiYdGwHdOa0ht0vzUWDlfioofFCRU0L+6NGDt6XzbgoJvZkMeRQ==}
+    engines: {node: '>=0.8.0'}
+
   has-ansi@2.0.0:
     resolution: {integrity: sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==}
     engines: {node: '>=0.10.0'}
@@ -8703,6 +8710,8 @@ snapshots:
     dependencies:
       duplexer: 0.1.2
 
+  hammerjs@2.0.8: {}
+
   has-ansi@2.0.0:
     dependencies:
       ansi-regex: 2.1.1

+ 182 - 63
src/components/PDFViewFull/index.vue

@@ -41,7 +41,7 @@
       </div>
       <div
         style="
-          height: 40px;
+          height: 60px;
           line-height: 40px;
           font-size: 30px;
           font-weight: 600;
@@ -49,10 +49,27 @@
           color: white;
           display: flex;
           justify-content: space-between;
+          align-items: center;
         "
       >
-        <div>共{{ pageLength }}页</div>
+        <div>共 {{ pageLength }} 页</div>
         <div>
+          到
+          <el-input-number
+            @click.stop
+            v-model="pageValue"
+            :max="pageLength < 1 ? 1 : pageLength"
+            :min="1"
+            style="background-color: white; border-radius: 5px"
+          />
+          页
+          <el-button
+            type="primary"
+            style="font-size: 20px; margin-right: 20px"
+            @click="scrollTo(pageValue)"
+            link
+            >跳转</el-button
+          >
           <el-button
             type="primary"
             style="font-size: 20px; margin-right: 20px"
@@ -64,7 +81,10 @@
       </div>
       <el-scrollbar
         ref="scrollbar"
-        style="height: calc(90vh - 40px); width: 90vw; padding: 0px 20px"
+        @scroll="bigScroll"
+        :always="true"
+        style="height: calc(90vh - 60px); width: 90vw; padding: 0px 30px"
+        :always-show="true"
       >
         <VuePdfEmbed
           v-if="showVisible"
@@ -84,20 +104,29 @@
         />
 
         <template v-for="(item, index) in pageLength" :key="index">
-          <el-scrollbar v-if="showELStatus">
-            <VuePdfEmbed
+          <div :id="'scrollbarId' + (index + 1)">
+            <el-scrollbar
               v-if="showELStatus"
-              :source="{
-                url: pdfSource,
-                cMapUrl: '/cmaps/',
-                cMapPacked: true,
-              }"
-              :page="index + 1"
-              @rendered="index + 1 === pageLength ? rendered() : null"
-              annotation-layer
-              text-layer
-            />
-          </el-scrollbar>
+              @scroll="(e) => scroll(e, index)"
+              :ref="(el) => scrollbarRefArray.push(el)"
+              style="padding: 20px"
+              :always-show="true"
+            >
+              <VuePdfEmbed
+                v-if="showELStatus"
+                :source="{
+                  url: pdfSource,
+                  cMapUrl: '/cmaps/',
+                  cMapPacked: true,
+                }"
+                :page="index + 1"
+                @rendered="index + 1 === pageLength ? rendered() : null"
+                annotation-layer
+                text-layer
+              />
+            </el-scrollbar>
+          </div>
+
           <div
             style="
               width: 100%;
@@ -118,6 +147,7 @@
 
 <script lang="ts" setup>
 import VuePdfEmbed from "vue-pdf-embed";
+import Hammer from "hammerjs";
 // essential styles
 import "vue-pdf-embed/dist/style/index.css";
 
@@ -161,7 +191,25 @@ const props = defineProps({
     default: 0,
   },
 });
+const value = ref(0);
+const pageValue = ref(1);
+const newBigScrollTop = ref(0);
+const bigScroll = ({ scrollTop }) => {
+  value.value = scrollTop;
+};
+const scrollTo = (page) => {
+  console.log(page);
+  const targetElement = document.getElementById(`scrollbarId${page}`);
+  console.log(targetElement);
+  if (targetElement) {
+    const topPos = targetElement.offsetTop;
+    console.log(topPos);
+    newBigScrollTop.value = topPos;
+    scrollbar.value.setScrollTop(topPos);
+  }
+};
 const scrollbar = ref(null);
+const scrollbarRefArray = ref([]);
 const reset = () => {
   scrollbar.value.setScrollTop(0);
   visible.value = false;
@@ -173,7 +221,6 @@ const pageStatus = ref(true);
 const renderedShow = (pdf: any) => {
   pageLength.value = pdf.numPages;
   pageStatus.value = false;
-  console.log(pageLength.value);
 };
 const visible = ref(false);
 const showVisible = ref(false);
@@ -182,6 +229,25 @@ const showPdf = () => {
   showVisible.value = true;
 };
 const showELStatus = ref(false);
+const leftArray = ref([]);
+const scroll = (e, index) => {
+  leftArray.value[index].scrollLeft = e.scrollLeft;
+};
+const setLeftArray = () => {
+  leftArray.value = [];
+  for (let i = 0; i < pageLength.value; i++) {
+    leftArray.value.push({
+      scrollLeft: 0,
+    });
+  }
+};
+watch(
+  pageLength,
+  () => {
+    setLeftArray();
+  },
+  { immediate: true }
+);
 const rendered = () => {
   loading.value = false;
 
@@ -189,100 +255,153 @@ const rendered = () => {
   const clickableElements = document.querySelectorAll(
     ".vue-pdf-embed .vue-pdf-embed__page"
   );
+
+  // 设置所有子元素居中对齐
   clickableElementsA.forEach((element) => {
     element.style.display = "flex";
     element.style.alignItems = "center";
     element.style.justifyContent = "center";
   });
+
   clickableElements.forEach((element, index) => {
+    element.index = index;
     let rotationAngle = 0;
-    element.addEventListener("click", () => {
+    element.style.touchAction = "auto";
+    element.addEventListener("dblclick", () => {
       const currentWidth = element.offsetWidth;
       const currentHeight = element.offsetHeight;
       const parent = element.parentNode;
-      let distance = 0;
-
-      distance = currentHeight - currentWidth;
+      let distance = currentHeight - currentWidth;
 
       rotationAngle = (rotationAngle + 90) % 360;
-      if (rotationAngle == 0) {
-        if (distance > 0) {
-          element.style.transform = `rotate(${rotationAngle}deg) `;
-          element.firstElementChild.style.transform = `translateY(0px) `;
-        } else {
-          element.style.transform = `rotate(${rotationAngle}deg) `;
-          element.firstElementChild.style.transform = `translateY(0px) `;
-        }
-      }
-      if (rotationAngle == 90) {
+
+      if (rotationAngle === 0 || rotationAngle === 180) {
+        element.style.transform = `rotate(${rotationAngle}deg)`;
+        element.firstElementChild.style.transform = `translateY(0px)`;
+      } else if (rotationAngle === 90) {
+        element.style.transform = `rotate(${rotationAngle}deg)`;
         if (distance > 0) {
-          element.style.transform = `rotate(${rotationAngle}deg)`;
-          element.firstElementChild.style.transform = `translateY(-${distance / 2}px) `;
+          element.firstElementChild.style.transform = `translateY(-${distance / 2}px)`;
         } else {
-          element.style.transform = `rotate(${rotationAngle}deg)`;
           element.firstElementChild.style.transform = `translateX(${-distance / 2}px)`;
         }
-      }
-      if (rotationAngle === 180) {
-        if (distance > 0) {
-          element.style.transform = `rotate(${rotationAngle}deg) `;
-          element.firstElementChild.style.transform = `translateY(0px) `;
-        } else {
-          element.style.transform = `rotate(${rotationAngle}deg) `;
-          element.firstElementChild.style.transform = `translateY(0px) `;
-        }
-      }
-      if (rotationAngle === 270) {
+      } else if (rotationAngle === 270) {
+        element.style.transform = `rotate(${rotationAngle}deg)`;
         if (distance > 0) {
-          element.style.transform = `rotate(${rotationAngle}deg) `;
-          element.firstElementChild.style.transform = `translateY(${distance / 2}px) `;
+          element.firstElementChild.style.transform = `translateY(${distance / 2}px)`;
         } else {
-          element.style.transform = `rotate(${rotationAngle}deg) `;
           element.firstElementChild.style.transform = `translateX(${distance / 2}px)`;
         }
       }
+
       parent.style.width = `${currentWidth}px`;
       parent.style.height = `${currentHeight}px`;
     });
+
+    // 🖱️ Ctrl + 鼠标滚轮 缩放
     element.addEventListener("wheel", function (event) {
       const currentWidth = element.offsetWidth;
       const currentHeight = element.offsetHeight;
       const parent = element.parentNode;
+
       if (event.ctrlKey) {
         event.preventDefault();
 
-        // 获取当前的transform值并解析
         const transform = getComputedStyle(element).transform;
         let scale = 1,
           rotate = 0;
 
         if (transform && transform !== "none") {
-          const transformMatrix = new WebKitCSSMatrix(transform);
-          scale = Math.sqrt(
-            Math.pow(transformMatrix.a, 2) + Math.pow(transformMatrix.b, 2)
-          );
-          rotate =
-            Math.atan2(transformMatrix.b, transformMatrix.a) * (180 / Math.PI);
+          const matrix = new WebKitCSSMatrix(transform);
+          scale = Math.sqrt(matrix.a * matrix.a + matrix.b * matrix.b);
+          rotate = Math.atan2(matrix.b, matrix.a) * (180 / Math.PI);
         }
 
-        // 根据滚轮方向调整缩放级别
         if (event.deltaY < 0) {
-          // 向上滚动,放大
-          scale += 0.1;
+          scale += 0.1; // 放大
         } else {
-          // 向下滚动,缩小
-          scale -= 0.1;
+          scale -= 0.1; // 缩小
         }
-        // 确保缩放级别不会变成负数或过小
         scale = Math.max(0.1, Math.min(scale, 5));
 
-        // 应用新的缩放级别和原有的旋转角度
         element.style.transform = `scale(${scale}) rotate(${rotate}deg)`;
-        //同步放大展示盒子
         parent.style.width = `${currentWidth * scale}px`;
         parent.style.height = `${currentHeight * scale}px`;
       }
     });
+
+    // 🔍 Hammer.js 只用于双指 Pinch(缩放)
+    const hammer = new Hammer(element);
+    hammer.get("pinch").set({ enable: true });
+
+    let initialScale = 1;
+
+    hammer.on("pinchstart", (ev) => {
+      const transform = getComputedStyle(element).transform;
+      if (transform && transform !== "none") {
+        const matrix = new WebKitCSSMatrix(transform);
+        initialScale = Math.sqrt(matrix.a * matrix.a + matrix.b * matrix.b);
+      }
+    });
+
+    hammer.on("pinch", (ev) => {
+      const parent = element.parentNode;
+
+      let scale = initialScale * ev.scale;
+      scale = Math.max(0.5, Math.min(scale, 5));
+
+      const transform = getComputedStyle(element).transform;
+      let rotate = 0;
+
+      if (transform && transform !== "none") {
+        const matrix = new WebKitCSSMatrix(transform);
+        rotate = Math.atan2(matrix.b, matrix.a) * (180 / Math.PI);
+      }
+
+      element.style.transform = `rotate(${rotate}deg) scale(${scale})`;
+      parent.style.width = `${element.offsetWidth * scale}px`;
+      parent.style.height = `${element.offsetHeight * scale}px`;
+    });
+    hammer.on("pinchend", () => {
+      console.log("Pinch 操作结束");
+    });
+    hammer.get("pan").set({
+      direction: Hammer.DIRECTION_VERTICAL,
+      threshold: 5, // 最小触发距离
+    });
+    let startX; // 记录开始触摸的位置
+    let startScrollLeft = 0; // 记录开始时的滚动位置
+    hammer.on("panstart", (ev) => {
+      startX = ev.center.x;
+    });
+
+    hammer.on("panmove", (ev) => {
+      const deltaX = -ev.deltaX; // 手指水平移动的距离
+      const deltaY = -ev.deltaY;
+      scrollbarRefArray.value[element.index].setScrollLeft(
+        startScrollLeft + deltaX
+      );
+      scrollbar.value.setScrollTop(newBigScrollTop.value + deltaY);
+      ev.preventDefault();
+    });
+    hammer.on("panend", (ev) => {
+      const deltaX = -ev.deltaX; // 手指水平移动的距离
+      const deltaY = -ev.deltaY;
+      startScrollLeft = startScrollLeft + deltaX;
+      if (startScrollLeft < 0) {
+        startScrollLeft = 0;
+      } else if (startScrollLeft > leftArray.value[element.index].scrollLeft) {
+        startScrollLeft = leftArray.value[element.index].scrollLeft;
+      }
+      newBigScrollTop.value = newBigScrollTop.value + deltaY;
+      if (newBigScrollTop.value < 0) {
+        newBigScrollTop.value = 0;
+      } else if (newBigScrollTop.value > value.value) {
+        newBigScrollTop.value = value.value;
+      }
+      // 阻止默认行为(如页面滚动)
+      ev.preventDefault();
+    });
   });
 };
 </script>

+ 1 - 1
src/views/pro-steps/index.vue

@@ -1040,7 +1040,7 @@
               :need-to-show-pdf="selectIndexData4 != null"
               content-type="button"
               :is-link="false"
-              pdf-source="http://192.168.1.3:20001/jgfile//2025/4/11/447519438742631.PDF"
+              pdf-source="http://192.168.1.3:20001/jgfile//2025/4/11/447488548627850.pdf"
             />
           </div>
         </div>