Jelajahi Sumber

企业资质图片改成向左滚动

gcz 1 bulan lalu
induk
melakukan
30648962e8
1 mengubah file dengan 213 tambahan dan 87 penghapusan
  1. 213 87
      src/pages/about/Index.vue

+ 213 - 87
src/pages/about/Index.vue

@@ -72,34 +72,58 @@
           </div>
           </div>
           
           
           <!-- 证书内容 -->
           <!-- 证书内容 -->
-          <div v-else>
-            <div class="certificate-slider"
-              :style="{ transform: `translateX(-${currentSlide * itemWidth * itemsPerPage}px)` }">
-              <div v-for="(certificate, index) in currentCertificates" :key="certificate.id || index" class="certificate-item">
-                <img :src="imgHost+certificate.image" :alt="certificate.title" @error="handleImageError" />
-                <!-- <p class="certificate-title">{{ certificate.title }}</p> -->
+          <div v-else class="certificate-display-area" ref="displayAreaRef">
+            <div 
+              class="certificate-scroll-wrapper" 
+              :class="{ 'is-scrolling': shouldScroll }"
+            >
+              <!-- 第一组证书 -->
+              <div class="certificate-group" ref="certGroupRef">
+                <div 
+                  v-for="(certificate, index) in currentCertificates" 
+                  :key="'orig-' + (certificate.id || index)" 
+                  class="certificate-item"
+                  @click="openPreview(certificate)"
+                >
+                  <img :src="imgHost+certificate.image" :alt="certificate.title" @error="handleImageError" />
+                  <!-- <p class="certificate-title">{{ certificate.title }}</p> -->
+                </div>
+              </div>
+              
+              <!-- 复制一组用于无缝滚动 -->
+              <div v-if="shouldScroll" class="certificate-group">
+                <div 
+                  v-for="(certificate, index) in currentCertificates" 
+                  :key="'copy-' + (certificate.id || index)" 
+                  class="certificate-item"
+                  @click="openPreview(certificate)"
+                >
+                  <img :src="imgHost+certificate.image" :alt="certificate.title" @error="handleImageError" />
+                  <!-- <p class="certificate-title">{{ certificate.title }}</p> -->
+                </div>
               </div>
               </div>
             </div>
             </div>
-
-            <!-- 左右切换按钮 -->
-            <button v-if="showNavButtons && currentSlide > 0" class="nav-btn nav-btn-left" @click="prevSlide">
-              <img src="@assets/slide-arrow-left.png" alt="上一页" />
-            </button>
-            <button v-if="showNavButtons && currentSlide < maxSlide" class="nav-btn nav-btn-right" @click="nextSlide">
-              <img src="@assets/slide-arrow-right.png" alt="下一页" />
-            </button>
           </div>
           </div>
         </div>
         </div>
       </div>
       </div>
+
+      <!-- 图片预览弹窗 -->
+      <div v-if="previewVisible" class="image-preview-modal" @click="closePreview">
+        <div class="preview-content" @click.stop>
+          <button class="close-btn" @click="closePreview">&times;</button>
+          <img :src="imgHost + previewImageSrc" alt="证书预览" />
+        </div>
+      </div>
     </section>
     </section>
   </DefaultLayout>
   </DefaultLayout>
 </template>
 </template>
 
 
 <script>
 <script>
-import { ref, onMounted, computed, onUnmounted } from 'vue'
+import { ref, onMounted, computed, onUnmounted, nextTick, watch } from 'vue'
 import { getCompanyCertList } from '@/api/modules/home'
 import { getCompanyCertList } from '@/api/modules/home'
 import DefaultLayout from '@/layouts/DefaultLayout.vue'
 import DefaultLayout from '@/layouts/DefaultLayout.vue'
 import { animateNumber, createNumberAnimationObserver } from '@/utils/numberAnimation'
 import { animateNumber, createNumberAnimationObserver } from '@/utils/numberAnimation'
+import { onScaleChange, scaleManager } from '@/utils/scaleManager'
 
 
 export default {
 export default {
   name: 'AboutPage',
   name: 'AboutPage',
@@ -111,15 +135,24 @@ export default {
     const imgHost = import.meta.env.VITE_APP_IMG_HOST
     const imgHost = import.meta.env.VITE_APP_IMG_HOST
     // 当前激活的标签页
     // 当前激活的标签页
     const activeTab = ref(0)
     const activeTab = ref(0)
-    // 当前滑动位置
-    const currentSlide = ref(0)
-    // 每页显示的证书数量
-    const itemsPerPage = ref(5)
-    // 每个证书项的宽度(包括间距)
-    const itemWidth = ref(220)
     // 加载状态
     // 加载状态
     const loading = ref(false)
     const loading = ref(false)
 
 
+    // 滚动相关
+    const displayAreaRef = ref(null)
+    const certGroupRef = ref(null)
+    const shouldScroll = ref(false)
+    const isPaused = ref(false)
+
+    // 预览相关
+    const previewVisible = ref(false)
+    const previewImageSrc = ref('')
+
+    // Scale Manager Callback
+    const handleScaleChange = () => {
+      nextTick(() => checkScroll())
+    }
+
     // 数据统计相关
     // 数据统计相关
     const dataStatsRef = ref(null)
     const dataStatsRef = ref(null)
     const animationObserver = ref(null)
     const animationObserver = ref(null)
@@ -166,34 +199,13 @@ export default {
       return certificateCategories.value[activeTab.value]?.certificates || []
       return certificateCategories.value[activeTab.value]?.certificates || []
     })
     })
 
 
-    // 是否显示导航按钮
-    const showNavButtons = computed(() => {
-      return currentCertificates.value.length > itemsPerPage.value
-    })
-
-    // 最大滑动页数
-    const maxSlide = computed(() => {
-      return Math.max(0, Math.ceil(currentCertificates.value.length / itemsPerPage.value) - 1)
-    })
-
     // 切换标签页
     // 切换标签页
     const switchTab = (index) => {
     const switchTab = (index) => {
       activeTab.value = index
       activeTab.value = index
-      currentSlide.value = 0 // 重置滑动位置
-    }
-
-    // 上一页
-    const prevSlide = () => {
-      if (currentSlide.value > 0) {
-        currentSlide.value--
-      }
-    }
-
-    // 下一页
-    const nextSlide = () => {
-      if (currentSlide.value < maxSlide.value) {
-        currentSlide.value++
-      }
+      // 切换tab后重新检查是否需要滚动
+      nextTick(() => {
+        checkScroll()
+      })
     }
     }
 
 
     // 图片加载错误处理
     // 图片加载错误处理
@@ -202,12 +214,47 @@ export default {
       event.target.src = 'https://via.placeholder.com/256x354?text=证书图片'
       event.target.src = 'https://via.placeholder.com/256x354?text=证书图片'
     }
     }
 
 
+    // 检查是否需要滚动
+    const checkScroll = () => {
+      if (!displayAreaRef.value || !certGroupRef.value) return
+      
+      const contentWidth = certGroupRef.value.scrollWidth
+      const containerWidth = displayAreaRef.value.clientWidth
+      
+      // 获取容器的padding
+      const style = window.getComputedStyle(displayAreaRef.value)
+      const paddingLeft = parseFloat(style.paddingLeft) || 0
+      const paddingRight = parseFloat(style.paddingRight) || 0
+      const availableWidth = containerWidth - paddingLeft - paddingRight
+      
+      // 如果内容宽度大于可用宽度,开启滚动
+      shouldScroll.value = contentWidth > availableWidth
+    }
+
+    // 监听窗口大小变化
+    const handleResize = () => {
+      checkScroll()
+    }
+
+    // 打开预览
+    const openPreview = (cert) => {
+      previewImageSrc.value = cert.image
+      previewVisible.value = true
+      document.body.style.overflow = 'hidden' // 禁止背景滚动
+    }
+
+    // 关闭预览
+    const closePreview = () => {
+      previewVisible.value = false
+      previewImageSrc.value = ''
+      document.body.style.overflow = '' // 恢复背景滚动
+    }
+
     // 加载证书数据
     // 加载证书数据
     const loadCertificates = async () => {
     const loadCertificates = async () => {
       try {
       try {
         loading.value = true
         loading.value = true
         const response = await getCompanyCertList({companyId:1})
         const response = await getCompanyCertList({companyId:1})
-        // console.log('证书数据',response)
         
         
         if (response && response.rows) {
         if (response && response.rows) {
           // 清空现有证书数据
           // 清空现有证书数据
@@ -226,13 +273,21 @@ export default {
                 certType: cert.certType
                 certType: cert.certType
               })
               })
             }
             }
-            // console.log('证书数据category',category)
           })
           })
+          
+          // 数据加载完成后检查滚动
+          // nextTick(() => {
+          //   checkScroll()
+          // })
         }
         }
       } catch (error) {
       } catch (error) {
         console.error('加载证书数据失败:', error)
         console.error('加载证书数据失败:', error)
       } finally {
       } finally {
         loading.value = false
         loading.value = false
+        // Ensure DOM is updated after loading becomes false
+        nextTick(() => {
+          checkScroll()
+        })
       }
       }
     }
     }
 
 
@@ -272,6 +327,12 @@ export default {
       loadCertificates()
       loadCertificates()
       // 延迟设置动画观察器,确保DOM已渲染
       // 延迟设置动画观察器,确保DOM已渲染
       setTimeout(setupStatsAnimation, 100)
       setTimeout(setupStatsAnimation, 100)
+      
+      // 添加resize监听
+      window.addEventListener('resize', handleResize)
+      
+      // 添加缩放监听
+      scaleManager.onScaleChange(handleScaleChange)
     })
     })
 
 
     onUnmounted(() => {
     onUnmounted(() => {
@@ -279,23 +340,29 @@ export default {
       if (animationObserver.value) {
       if (animationObserver.value) {
         animationObserver.value.disconnect()
         animationObserver.value.disconnect()
       }
       }
+      window.removeEventListener('resize', handleResize)
+      
+      // 移除缩放监听
+      scaleManager.removeScaleChangeListener(handleScaleChange)
     })
     })
 
 
     return {
     return {
       imgHost,
       imgHost,
       activeTab,
       activeTab,
-      currentSlide,
-      itemWidth,
-      itemsPerPage,
       loading,
       loading,
       certificateCategories,
       certificateCategories,
       currentCertificates,
       currentCertificates,
-      showNavButtons,
-      maxSlide,
       switchTab,
       switchTab,
-      prevSlide,
-      nextSlide,
       handleImageError,
       handleImageError,
+      // 滚动相关
+      displayAreaRef,
+      certGroupRef,
+      shouldScroll,
+      // 预览相关
+      previewVisible,
+      previewImageSrc,
+      openPreview,
+      closePreview,
       // 数字动画相关
       // 数字动画相关
       dataStatsRef,
       dataStatsRef,
       animatedStats
       animatedStats
@@ -438,14 +505,17 @@ export default {
   .certificate-container {
   .certificate-container {
     position: relative;
     position: relative;
     overflow: hidden;
     overflow: hidden;
-    padding: 60px; // 为导航按钮留出空间
+    padding: 60px 0; // 调整padding
     box-shadow: 0 8px 30px rgba(0, 0, 0, 0.15);
     box-shadow: 0 8px 30px rgba(0, 0, 0, 0.15);
     background-color: rgba(255, 255, 255, 0.9);
     background-color: rgba(255, 255, 255, 0.9);
     border-radius: 10px;
     border-radius: 10px;
     min-height: 400px;
     min-height: 400px;
+    display: flex;
+    align-items: center;
 
 
     // 加载状态
     // 加载状态
     .loading-state {
     .loading-state {
+      width: 100%;
       display: flex;
       display: flex;
       flex-direction: column;
       flex-direction: column;
       align-items: center;
       align-items: center;
@@ -471,6 +541,7 @@ export default {
 
 
     // 空状态
     // 空状态
     .empty-state {
     .empty-state {
+      width: 100%;
       display: flex;
       display: flex;
       align-items: center;
       align-items: center;
       justify-content: center;
       justify-content: center;
@@ -483,10 +554,33 @@ export default {
       }
       }
     }
     }
 
 
-    .certificate-slider {
+    // 滚动区域容器
+    .certificate-display-area {
+      width: 100%;
+      overflow: hidden;
+      padding: 0 20px;
+    }
+
+    // 滚动包装器
+    .certificate-scroll-wrapper {
+      display: flex;
+      width: max-content;
+      
+      &.is-scrolling {
+        animation: scroll 30s linear infinite;
+        
+        &:hover {
+          animation-play-state: paused;
+        }
+      }
+    }
+
+    // 证书组
+    .certificate-group {
       display: flex;
       display: flex;
-      transition: transform 0.5s ease;
       gap: 44px;
       gap: 44px;
+      padding-right: 44px; // 确保组之间有间距
+      flex-shrink: 0;
     }
     }
 
 
     .certificate-item {
     .certificate-item {
@@ -497,6 +591,7 @@ export default {
       // padding: 20px;
       // padding: 20px;
       box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
       box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
       transition: all 0.3s ease;
       transition: all 0.3s ease;
+      cursor: pointer; // 添加手型光标
 
 
       &:hover {
       &:hover {
         transform: translateY(-5px);
         transform: translateY(-5px);
@@ -520,44 +615,75 @@ export default {
         margin: 0;
         margin: 0;
       }
       }
     }
     }
+  }
+}
+
+// 图片预览弹窗
+.image-preview-modal {
+  position: fixed;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  background: rgba(0, 0, 0, 0.85);
+  z-index: 9999;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  padding: 40px;
+  animation: fadeIn 0.3s ease;
+
+  .preview-content {
+    position: relative;
+    max-width: 90%;
+    max-height: 90vh;
+    animation: zoomIn 0.3s ease;
+
+    img {
+      max-width: 100%;
+      max-height: 90vh;
+      object-fit: contain;
+      border-radius: 4px;
+      box-shadow: 0 0 20px rgba(0,0,0,0.5);
+    }
 
 
-    // 导航按钮
-    .nav-btn {
+    .close-btn {
       position: absolute;
       position: absolute;
-      top: 50%;
-      transform: translateY(-50%);
-      width: 40px;
-      height: 40px;
-      background: rgba(255, 255, 255, 0.9);
-      border: 1px solid #e0e0e0;
-      border-radius: 50%;
+      top: -40px;
+      right: -40px;
+      background: none;
+      border: none;
+      color: #fff;
+      font-size: 40px;
       cursor: pointer;
       cursor: pointer;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-      transition: all 0.3s ease;
-      z-index: 10;
+      padding: 0;
+      line-height: 1;
+      transition: transform 0.3s ease;
 
 
       &:hover {
       &:hover {
-        background: #fff;
-        box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
-        transform: translateY(-50%) scale(1.1);
+        transform: rotate(90deg);
       }
       }
+    }
+  }
+}
 
 
-      img {
-        width: 16px;
-        height: 16px;
-      }
+@keyframes scroll {
+  0% {
+    transform: translateX(0);
+  }
+  100% {
+    transform: translateX(-50%);
+  }
+}
 
 
-      &.nav-btn-left {
-        left: 10px;
-      }
+@keyframes fadeIn {
+  from { opacity: 0; }
+  to { opacity: 1; }
+}
 
 
-      &.nav-btn-right {
-        right: 10px;
-      }
-    }
-  }
+@keyframes zoomIn {
+  from { transform: scale(0.9); opacity: 0; }
+  to { transform: scale(1); opacity: 1; }
 }
 }
 
 
 // 响应式设计
 // 响应式设计