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