解决了加载商品时网页频繁闪烁的问题

This commit is contained in:
2025-08-26 16:15:46 +08:00
parent b96911bd15
commit 9503080c85

View File

@@ -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;