From 617ad108a3c7f4e676229b1495185e66fac644b7 Mon Sep 17 00:00:00 2001
From: zhangwencui <1064582902@qq.com>
Date: 星期三, 25 三月 2026 17:11:02 +0800
Subject: [PATCH] 销售统计看板自适应,以及中心背景替换
---
src/views/reportAnalysis/qualityAnalysis/components/CarouselCards.vue | 524 ++++++++++++++++++++++++++++++----------------------------
1 files changed, 271 insertions(+), 253 deletions(-)
diff --git a/src/views/reportAnalysis/qualityAnalysis/components/CarouselCards.vue b/src/views/reportAnalysis/qualityAnalysis/components/CarouselCards.vue
index 0498824..922d7d4 100644
--- a/src/views/reportAnalysis/qualityAnalysis/components/CarouselCards.vue
+++ b/src/views/reportAnalysis/qualityAnalysis/components/CarouselCards.vue
@@ -1,306 +1,324 @@
<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 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="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">
+ <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 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'
+ 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 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 cardsContainerRef = ref(null);
+ const currentScrollLeft = ref(0);
+ const maxScrollLeft = ref(0);
-// 妫�鏌ユ槸鍚﹀彲浠ュ悜宸︽粴鍔�
-const canScrollLeft = computed(() => {
- return currentScrollLeft.value > 0
-})
+ // 妫�鏌ユ槸鍚﹀彲浠ュ悜宸︽粴鍔�
+ const canScrollLeft = computed(() => {
+ return currentScrollLeft.value > 0;
+ });
-// 妫�鏌ユ槸鍚﹀彲浠ュ悜鍙虫粴鍔�
-const canScrollRight = computed(() => {
- return currentScrollLeft.value < maxScrollLeft.value
-})
+ // 妫�鏌ユ槸鍚﹀彲浠ュ悜鍙虫粴鍔�
+ 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 updateScrollState = () => {
+ const container = cardsContainerRef.value;
+ if (!container) return;
-// 鍚戝乏婊氬姩
-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)
-}
+ currentScrollLeft.value = container.scrollLeft;
+ maxScrollLeft.value = container.scrollWidth - container.clientWidth;
+ };
-// 鍚戝彸婊氬姩
-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)
-}
+ // 鍚戝乏婊氬姩
+ const scrollLeftFn = () => {
+ const container = cardsContainerRef.value;
+ if (!container) return;
-// 鐩戝惉 items 鍙樺寲锛屾洿鏂版粴鍔ㄧ姸鎬�
-watch(() => props.items, () => {
- nextTick(() => {
- updateScrollState()
- })
-}, { deep: true })
+ const scrollItems = Array.from(container.querySelectorAll(".card-item"));
+ if (scrollItems.length === 0) return;
-onMounted(() => {
- nextTick(() => {
- updateScrollState()
- // 鐩戝惉婊氬姩浜嬩欢
- const container = cardsContainerRef.value
+ 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.addEventListener('scroll', updateScrollState)
+ container.removeEventListener("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;
-}
+ .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 {
+ 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 */
-}
+ .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 {
+ 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:hover {
+ background: rgba(26, 88, 176, 0.8);
+ transform: translateY(-50%) scale(1.1);
+ }
-.nav-button-left {
- left: -16px;
-}
+ .nav-button-left {
+ left: -16px;
+ }
-.nav-button-left img {
- width: 16px;
- height: 16px;
- transform: rotate(180deg);
-}
+ .nav-button-left img {
+ width: 16px;
+ height: 16px;
+ transform: rotate(180deg);
+ }
-.nav-button-right {
- right: -16px;
-}
+ .nav-button-right {
+ right: -16px;
+ }
-.nav-button-right img {
- width: 16px;
- height: 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 {
+ 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-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-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-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-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-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);
-}
+ .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-label {
+ opacity: 0.85;
+ }
-.rate-value {
- font-weight: 500;
-}
+ .rate-value {
+ font-weight: 500;
+ }
-.value-number {
- font-weight: 400;
- font-size: 14px;
- color: #FFFFFF;
- line-height: 1;
-}
+ .value-number {
+ font-weight: 400;
+ font-size: 14px;
+ color: #ffffff;
+ line-height: 1;
+ }
-.value-unit {
- font-size: 14px;
- color: #FFFFFF;
- font-weight: 400;
-}
+ .value-unit {
+ font-size: 14px;
+ color: #ffffff;
+ font-weight: 400;
+ }
</style>
--
Gitblit v1.9.3