From 9503080c8548995d19f6cbe95e6a9e4ed8d033b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A4=A7=E8=84=8F=E7=8B=BC?= <786316265@qq.com> Date: Tue, 26 Aug 2025 16:15:46 +0800 Subject: [PATCH] =?UTF-8?q?=E8=A7=A3=E5=86=B3=E4=BA=86=E5=8A=A0=E8=BD=BD?= =?UTF-8?q?=E5=95=86=E5=93=81=E6=97=B6=E7=BD=91=E9=A1=B5=E9=A2=91=E7=B9=81?= =?UTF-8?q?=E9=97=AA=E7=83=81=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/ProductSummary.vue | 123 +++++++++++++++++++++++++++++++++-- 1 file changed, 116 insertions(+), 7 deletions(-) diff --git a/src/views/ProductSummary.vue b/src/views/ProductSummary.vue index bbac9db..deade2b 100644 --- a/src/views/ProductSummary.vue +++ b/src/views/ProductSummary.vue @@ -8,7 +8,16 @@
+
+ - @@ -74,6 +91,8 @@ const products = ref([]) const firstProduct = ref([]) const productDetail = ref([]) const productDetailsCache = ref({}) // 缓存所有商品详情 +const loading = ref(true) // 添加加载状态 +const detailsLoaded = ref(false) // 商品详情是否加载完成 const route = useRoute() const productId = ref(null) @@ -130,6 +149,8 @@ const getFirstProduct = async () => { } catch (error) { ElMessage.error('获取商品详情失败') console.log(error) + } finally { + loading.value = false } } @@ -159,21 +180,44 @@ const getProductDetail = async (productId) => { const loadAllProductDetails = async () => { const uniqueProductIds = [...new Set(products.value.map(p => p.id))] - for (const id of uniqueProductIds) { + // 并行加载所有商品详情,避免逐个加载时的多次渲染 + const loadPromises = uniqueProductIds.map(async (id) => { try { - await getProductDetail(id) + const response = await api.get(`/products/${id}`) + if (response.data && response.data.data && response.data.data.product) { + return { id, product: response.data.data.product } + } } catch (error) { console.warn(`Failed to load details for product ${id}:`, error) + return null } - } + }) + + // 等待所有请求完成 + const results = await Promise.all(loadPromises) + + // 一次性更新缓存,避免多次触发响应式更新 + const newCache = { ...productDetailsCache.value } + results.forEach(result => { + if (result) { + newCache[result.id] = result.product + } + }) + + // 一次性更新缓存和加载状态 + productDetailsCache.value = newCache + detailsLoaded.value = true } // 添加生命周期钩子来调用函数 -onMounted(() => { +onMounted(async () => { productId.value = route.params.id console.log('Product ID:', productId.value) - getProducts() - getFirstProduct() + loading.value = true + + // 先加载第一个商品,再加载商品列表,确保数据同步 + await getFirstProduct() + await getProducts() }) @@ -351,6 +395,71 @@ onMounted(() => { } /* 修改部分结束 */ +/* 骨架屏样式 */ +.carousel-skeleton { + position: relative; + width: 100%; + height: 300px; + border-radius: 8px; + overflow: hidden; + background: #f5f5f5; + display: flex; + align-items: center; + justify-content: center; +} + +.skeleton-image { + width: 80%; + height: 80%; + background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%); + background-size: 200% 100%; + animation: skeleton-loading 1.5s infinite; + border-radius: 8px; +} + +.skeleton-indicators { + position: absolute; + bottom: 15px; + left: 50%; + transform: translateX(-50%); + display: flex; + gap: 8px; +} + +.skeleton-dot { + width: 8px; + height: 8px; + border-radius: 50%; + background: #d0d0d0; + animation: skeleton-pulse 1.5s infinite; +} + +.skeleton-dot:nth-child(2) { + animation-delay: 0.2s; +} + +.skeleton-dot:nth-child(3) { + animation-delay: 0.4s; +} + +@keyframes skeleton-loading { + 0% { + background-position: -200% 0; + } + 100% { + background-position: 200% 0; + } +} + +@keyframes skeleton-pulse { + 0%, 100% { + opacity: 0.4; + } + 50% { + opacity: 0.8; + } +} + /* 轮播图样式调整 */ :deep(.el-carousel) { border-radius: 8px;