zhangwencui
9 小时以前 617ad108a3c7f4e676229b1495185e66fac644b7
src/views/reportAnalysis/qualityAnalysis/components/CarouselCards.vue
@@ -1,160 +1,170 @@
<template>
  <div class="carousel-cards">
    <button
      v-if="canScrollLeft"
    <button v-if="canScrollLeft"
      class="nav-button nav-button-left"
      @click="scrollLeftFn"
    >
      <img src="@/assets/BI/jiantou.png" alt="左箭头" />
            @click="scrollLeftFn">
      <img src="@/assets/BI/jiantou.png"
           alt="左箭头" />
    </button>
    <div
      class="cards-container"
    <div class="cards-container"
      :style="{ '--visible-count': visibleCount }"
      ref="cardsContainerRef"
    >
      <div
        v-for="(item, index) in items"
         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>
           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">
          <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"
    <button v-if="canScrollRight"
      class="nav-button nav-button-right"
      @click="scrollRightFn"
    >
      <img src="@/assets/BI/jiantou.png" alt="右箭头" />
            @click="scrollRightFn">
      <img src="@/assets/BI/jiantou.png"
           alt="右箭头" />
    </button>
  </div>
</template>
<script setup>
import { ref, onMounted, onBeforeUnmount, nextTick, watch, computed } from 'vue'
  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'
      )
    }
      validator: value => {
        return value.every(
          item =>
            item &&
            typeof item.label !== "undefined" &&
            typeof item.value !== "undefined" &&
            typeof item.unit !== "undefined"
        );
      },
  },
  visibleCount: {
    type: Number,
    default: 3
  }
})
      default: 3,
    },
  });
const cardsContainerRef = ref(null)
const currentScrollLeft = ref(0)
const maxScrollLeft = ref(0)
  const cardsContainerRef = ref(null);
  const currentScrollLeft = ref(0);
  const maxScrollLeft = ref(0);
// 检查是否可以向左滚动
const canScrollLeft = computed(() => {
  return currentScrollLeft.value > 0
})
    return currentScrollLeft.value > 0;
  });
// 检查是否可以向右滚动
const canScrollRight = computed(() => {
  return currentScrollLeft.value < maxScrollLeft.value
})
    return currentScrollLeft.value < maxScrollLeft.value;
  });
// 更新滚动状态
const updateScrollState = () => {
  const container = cardsContainerRef.value
  if (!container) return
    const container = cardsContainerRef.value;
    if (!container) return;
  
  currentScrollLeft.value = container.scrollLeft
  maxScrollLeft.value = container.scrollWidth - container.clientWidth
}
    currentScrollLeft.value = container.scrollLeft;
    maxScrollLeft.value = container.scrollWidth - container.clientWidth;
  };
// 向左滚动
const scrollLeftFn = () => {
  const container = cardsContainerRef.value
  if (!container) return
    const container = cardsContainerRef.value;
    if (!container) return;
  
  const scrollItems = Array.from(container.querySelectorAll('.card-item'))
  if (scrollItems.length === 0) 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
    const itemWidth = scrollItems[0]?.offsetWidth || 0;
    const gap = 12;
    const scrollDistance = itemWidth + gap;
  
  container.scrollBy({
    left: -scrollDistance,
    behavior: 'smooth'
  })
      behavior: "smooth",
    });
  
  // 延迟更新状态,等待滚动动画完成
  setTimeout(() => {
    updateScrollState()
  }, 300)
}
      updateScrollState();
    }, 300);
  };
// 向右滚动
const scrollRightFn = () => {
  const container = cardsContainerRef.value
  if (!container) return
    const container = cardsContainerRef.value;
    if (!container) return;
  
  const scrollItems = Array.from(container.querySelectorAll('.card-item'))
  if (scrollItems.length === 0) 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
    const itemWidth = scrollItems[0]?.offsetWidth || 0;
    const gap = 12;
    const scrollDistance = itemWidth + gap;
  
  container.scrollBy({
    left: scrollDistance,
    behavior: 'smooth'
  })
      behavior: "smooth",
    });
  
  // 延迟更新状态,等待滚动动画完成
  setTimeout(() => {
    updateScrollState()
  }, 300)
}
      updateScrollState();
    }, 300);
  };
// 监听 items 变化,更新滚动状态
watch(() => props.items, () => {
  watch(
    () => props.items,
    () => {
  nextTick(() => {
    updateScrollState()
  })
}, { deep: true })
        updateScrollState();
      });
    },
    { deep: true }
  );
onMounted(() => {
  nextTick(() => {
    updateScrollState()
      updateScrollState();
    // 监听滚动事件
    const container = cardsContainerRef.value
      const container = cardsContainerRef.value;
    if (container) {
      container.addEventListener('scroll', updateScrollState)
        container.addEventListener("scroll", updateScrollState);
    }
  })
})
    });
  });
onBeforeUnmount(() => {
  // 清理滚动事件监听器
  const container = cardsContainerRef.value
    const container = cardsContainerRef.value;
  if (container) {
    container.removeEventListener('scroll', updateScrollState)
      container.removeEventListener("scroll", updateScrollState);
  }
})
  });
</script>
<style scoped>
@@ -225,11 +235,19 @@
}
.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));
    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%);
    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;
@@ -259,7 +277,7 @@
.card-label {
  font-weight: 400;
  font-size: 14px;
  color: #FFFFFF;
    color: #ffffff;
  margin-bottom: 4px;
  white-space: nowrap;
  overflow: hidden;
@@ -294,13 +312,13 @@
.value-number {
  font-weight: 400;
  font-size: 14px;
  color: #FFFFFF;
    color: #ffffff;
  line-height: 1;
}
.value-unit {
  font-size: 14px;
  color: #FFFFFF;
    color: #ffffff;
  font-weight: 400;
}
</style>