解决了加载商品时网页频繁闪烁的问题
This commit is contained in:
		| @@ -8,7 +8,16 @@ | ||||
|   <div class="products-container"> | ||||
|     <div class="product-card"> | ||||
|       <div class="product-image"> | ||||
|         <div v-if="loading || !firstProduct.images" class="carousel-skeleton"> | ||||
|           <div class="skeleton-image"></div> | ||||
|           <div class="skeleton-indicators"> | ||||
|             <div class="skeleton-dot"></div> | ||||
|             <div class="skeleton-dot"></div> | ||||
|             <div class="skeleton-dot"></div> | ||||
|           </div> | ||||
|         </div> | ||||
|         <el-carousel | ||||
|             v-else | ||||
|             :interval="4000" | ||||
|             indicator-position="outside" | ||||
|             style="min-height: 300px;" | ||||
| @@ -35,12 +44,20 @@ | ||||
|     <div v-for="product in products.filter(p => p.id !== firstProduct.id)" :key="product.id" class="product-card"> | ||||
|       <!-- 轮播图部分 --> | ||||
|       <div class="product-image"> | ||||
|         <div v-if="!detailsLoaded" class="carousel-skeleton"> | ||||
|           <div class="skeleton-image"></div> | ||||
|           <div class="skeleton-indicators"> | ||||
|             <div class="skeleton-dot"></div> | ||||
|             <div class="skeleton-dot"></div> | ||||
|             <div class="skeleton-dot"></div> | ||||
|           </div> | ||||
|         </div> | ||||
|         <el-carousel | ||||
|           v-else | ||||
|           :interval="4000" | ||||
|           indicator-position="outside" | ||||
|           style="min-height: 300px;" | ||||
|         > | ||||
|           <!-- <el-carousel-item v-for="(image, index) in product.images" :key="index"> --> | ||||
|           <el-carousel-item v-for="(image, index) in (productDetailsCache[product.id]?.images || [])" :key="index"> | ||||
|             <img :src="image" :alt="product.name" class="carousel-image" /> | ||||
|           </el-carousel-item> | ||||
| @@ -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() | ||||
| }) | ||||
|  | ||||
| </script> | ||||
| @@ -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; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user