From 9d485123b8c947f61c94aee67a0ceec8953a510d Mon Sep 17 00:00:00 2001
From: yaowanxin <3588231647@qq.com>
Date: 星期五, 30 一月 2026 17:36:56 +0800
Subject: [PATCH] 用印管理、规章制度管理页面添加分页功能

---
 src/views/reportAnalysis/productionAnalysis/components/CarouselCards.vue |  306 +++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 306 insertions(+), 0 deletions(-)

diff --git a/src/views/reportAnalysis/productionAnalysis/components/CarouselCards.vue b/src/views/reportAnalysis/productionAnalysis/components/CarouselCards.vue
new file mode 100644
index 0000000..0498824
--- /dev/null
+++ b/src/views/reportAnalysis/productionAnalysis/components/CarouselCards.vue
@@ -0,0 +1,306 @@
+<template>
+  <div class="carousel-cards">
+    <button 
+      v-if="canScrollLeft"
+      class="nav-button nav-button-left"
+      @click="scrollLeftFn"
+    >
+      <img src="@/assets/BI/jiantou.png" alt="宸︾澶�" />
+    </button>
+    <div 
+      class="cards-container" 
+      :style="{ '--visible-count': visibleCount }"
+      ref="cardsContainerRef"
+    >
+      <div
+        v-for="(item, index) in items"
+        :key="index"
+        class="card-item"
+      >
+        <div v-if="item.icon" class="card-icon" :style="{ backgroundImage: `url(${item.icon})` }"></div>
+        <div class="card-title">
+          <div class="card-label">{{ item.label }}</div>
+          <div class="card-value">
+            <span class="value-number">{{ item.value }}</span>
+            <span class="value-unit">{{ item.unit }}</span>
+          </div>
+          <div v-if="item.rate ?? item.ratio ?? item.percent" class="card-rate">
+            <span class="rate-value">{{ item.rate ?? item.ratio ?? item.percent }}%</span>
+          </div>
+        </div>
+      </div>
+    </div>
+    <button 
+      v-if="canScrollRight"
+      class="nav-button nav-button-right"
+      @click="scrollRightFn"
+    >
+      <img src="@/assets/BI/jiantou.png" alt="鍙崇澶�" />
+    </button>
+  </div>
+</template>
+
+<script setup>
+import { ref, onMounted, onBeforeUnmount, nextTick, watch, computed } from 'vue'
+
+const props = defineProps({
+  items: {
+    type: Array,
+    default: () => [],
+    validator: (value) => {
+      return value.every(item => 
+        item && typeof item.label !== 'undefined' && 
+        typeof item.value !== 'undefined' && 
+        typeof item.unit !== 'undefined'
+      )
+    }
+  },
+  visibleCount: {
+    type: Number,
+    default: 3
+  }
+})
+
+const cardsContainerRef = ref(null)
+const currentScrollLeft = ref(0)
+const maxScrollLeft = ref(0)
+
+// 妫�鏌ユ槸鍚﹀彲浠ュ悜宸︽粴鍔�
+const canScrollLeft = computed(() => {
+  return currentScrollLeft.value > 0
+})
+
+// 妫�鏌ユ槸鍚﹀彲浠ュ悜鍙虫粴鍔�
+const canScrollRight = computed(() => {
+  return currentScrollLeft.value < maxScrollLeft.value
+})
+
+// 鏇存柊婊氬姩鐘舵��
+const updateScrollState = () => {
+  const container = cardsContainerRef.value
+  if (!container) return
+  
+  currentScrollLeft.value = container.scrollLeft
+  maxScrollLeft.value = container.scrollWidth - container.clientWidth
+}
+
+// 鍚戝乏婊氬姩
+const scrollLeftFn = () => {
+  const container = cardsContainerRef.value
+  if (!container) return
+  
+  const scrollItems = Array.from(container.querySelectorAll('.card-item'))
+  if (scrollItems.length === 0) return
+  
+  const itemWidth = scrollItems[0]?.offsetWidth || 0
+  const gap = 12
+  const scrollDistance = itemWidth + gap
+  
+  container.scrollBy({
+    left: -scrollDistance,
+    behavior: 'smooth'
+  })
+  
+  // 寤惰繜鏇存柊鐘舵�侊紝绛夊緟婊氬姩鍔ㄧ敾瀹屾垚
+  setTimeout(() => {
+    updateScrollState()
+  }, 300)
+}
+
+// 鍚戝彸婊氬姩
+const scrollRightFn = () => {
+  const container = cardsContainerRef.value
+  if (!container) return
+  
+  const scrollItems = Array.from(container.querySelectorAll('.card-item'))
+  if (scrollItems.length === 0) return
+  
+  const itemWidth = scrollItems[0]?.offsetWidth || 0
+  const gap = 12
+  const scrollDistance = itemWidth + gap
+  
+  container.scrollBy({
+    left: scrollDistance,
+    behavior: 'smooth'
+  })
+  
+  // 寤惰繜鏇存柊鐘舵�侊紝绛夊緟婊氬姩鍔ㄧ敾瀹屾垚
+  setTimeout(() => {
+    updateScrollState()
+  }, 300)
+}
+
+// 鐩戝惉 items 鍙樺寲锛屾洿鏂版粴鍔ㄧ姸鎬�
+watch(() => props.items, () => {
+  nextTick(() => {
+    updateScrollState()
+  })
+}, { deep: true })
+
+onMounted(() => {
+  nextTick(() => {
+    updateScrollState()
+    // 鐩戝惉婊氬姩浜嬩欢
+    const container = cardsContainerRef.value
+    if (container) {
+      container.addEventListener('scroll', updateScrollState)
+    }
+  })
+})
+
+onBeforeUnmount(() => {
+  // 娓呯悊婊氬姩浜嬩欢鐩戝惉鍣�
+  const container = cardsContainerRef.value
+  if (container) {
+    container.removeEventListener('scroll', updateScrollState)
+  }
+})
+</script>
+
+<style scoped>
+.carousel-cards {
+  width: 100%;
+  overflow: hidden;
+  position: relative;
+  display: flex;
+  align-items: center;
+}
+
+.cards-container {
+  display: flex;
+  gap: 12px;
+  width: 100%;
+  overflow-x: auto;
+  overflow-y: hidden;
+  scrollbar-width: none; /* Firefox */
+  -ms-overflow-style: none; /* IE and Edge */
+  padding-bottom: 4px;
+  scroll-behavior: smooth;
+}
+
+.cards-container::-webkit-scrollbar {
+  display: none; /* Chrome, Safari, Opera */
+}
+
+.nav-button {
+  position: absolute;
+  top: 50%;
+  transform: translateY(-50%);
+  width: 32px;
+  height: 32px;
+  background: rgba(26, 88, 176, 0.6);
+  border: 1px solid rgba(26, 88, 176, 0.8);
+  border-radius: 50%;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  cursor: pointer;
+  z-index: 10;
+  transition: all 0.3s ease;
+  padding: 0;
+}
+
+.nav-button:hover {
+  background: rgba(26, 88, 176, 0.8);
+  transform: translateY(-50%) scale(1.1);
+}
+
+.nav-button-left {
+  left: -16px;
+}
+
+.nav-button-left img {
+  width: 16px;
+  height: 16px;
+  transform: rotate(180deg);
+}
+
+.nav-button-right {
+  right: -16px;
+}
+
+.nav-button-right img {
+  width: 16px;
+  height: 16px;
+}
+
+.card-item {
+  flex: 0 0 calc((100% - (var(--visible-count) - 1) * 12px) / var(--visible-count));
+  min-width: calc((100% - (var(--visible-count) - 1) * 12px) / var(--visible-count));
+  display: flex;
+  align-items: center;
+  background: linear-gradient(269deg, rgba(27,57,126,0.13) 0%, rgba(33,137,206,0.33) 98.13%, #24AFF4 100%);
+  border-radius: 8px 8px 8px 8px;
+  padding: 12px 16px;
+  transition: all 0.3s ease;
+}
+
+.card-item:hover {
+  transform: translateY(-2px);
+}
+
+.card-icon {
+  width: 80px;
+  height: 60px;
+  background-size: cover;
+  background-position: center;
+  background-repeat: no-repeat;
+  flex-shrink: 0;
+  margin-right: 12px;
+}
+
+.card-title {
+  display: flex;
+  align-items: flex-start;
+  flex-direction: column;
+  flex: 1;
+}
+
+.card-label {
+  font-weight: 400;
+  font-size: 14px;
+  color: #FFFFFF;
+  margin-bottom: 4px;
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  width: 100%;
+}
+
+.card-value {
+  display: flex;
+  align-items: baseline;
+  gap: 4px;
+}
+
+.card-rate {
+  margin-top: 4px;
+  display: flex;
+  align-items: center;
+  gap: 6px;
+  font-weight: 400;
+  font-size: 12px;
+  color: rgba(255, 255, 255, 0.85);
+}
+
+.rate-label {
+  opacity: 0.85;
+}
+
+.rate-value {
+  font-weight: 500;
+}
+
+.value-number {
+  font-weight: 400;
+  font-size: 14px;
+  color: #FFFFFF;
+  line-height: 1;
+}
+
+.value-unit {
+  font-size: 14px;
+  color: #FFFFFF;
+  font-weight: 400;
+}
+</style>

--
Gitblit v1.9.3