添加首页加载skeleton

This commit is contained in:
2026-05-26 17:04:04 +08:00
parent 9680276b3f
commit 2f528c049f
10 changed files with 1329 additions and 410 deletions

View File

@@ -100,9 +100,11 @@
@click="emit('select-product', product)"
>
<view class="hmall-product-image-wrapper hmall-product-image-wrapper-fixed">
<view class="hmall-product-image-placeholder"></view>
<image
class="hmall-product-image"
:class="['hmall-product-image', isProductImageLoaded(product.id) ? 'hmall-product-image-loaded' : '']"
:src="getProductCover(product)"
@load="() => handleProductImageLoad(product.id)"
@error="() => handleProductImageError(product.id)"
mode="aspectFill"
/>
@@ -137,11 +139,11 @@
</view>
</view>
<view v-else-if="loading" class="hmall-loading-state">
<view v-else-if="pageLoading || loading" class="hmall-loading-state">
<text class="hmall-loading-text">正在加载商品...</text>
</view>
<view v-else class="hmall-empty-wrap">
<view v-else-if="!pageLoading" class="hmall-empty-wrap">
<view v-if="emptyStateDescription != ''" class="hmall-empty-state-extended">
<EmptyState :text="emptyStateTitle"></EmptyState>
<text class="hmall-empty-desc">{{ emptyStateDescription }}</text>
@@ -168,6 +170,7 @@ import type { MarketingChannel, ChannelProduct, SimpleCategoryChannel } from '@/
const failedProductImageIds = ref<string[]>([])
const failedChannelImageIds = ref<string[]>([])
const loadedProductImageIds = ref<string[]>([])
type SecondaryCategoryPage = {
id: string
@@ -199,6 +202,10 @@ const props = defineProps({
type: Array<Product>,
default: [] as Array<Product>
},
pageLoading: {
type: Boolean,
default: false
},
loading: {
type: Boolean,
default: false
@@ -339,11 +346,28 @@ function handleProductImageError(productId: string): void {
if (productId == '') {
return
}
handleProductImageLoad(productId)
if (failedProductImageIds.value.indexOf(productId) == -1) {
failedProductImageIds.value.push(productId)
}
}
function handleProductImageLoad(productId: string): void {
if (productId == '') {
return
}
if (loadedProductImageIds.value.indexOf(productId) == -1) {
loadedProductImageIds.value.push(productId)
}
}
function isProductImageLoaded(productId: string): boolean {
if (productId == '') {
return false
}
return loadedProductImageIds.value.indexOf(productId) != -1
}
function getProductTitle(product: Product): string {
if (product.short_title != null && product.short_title != '') {
return product.short_title
@@ -777,6 +801,17 @@ function showMarketPrice(product: Product): boolean {
background: #f2f2f2;
}
.hmall-product-image-placeholder {
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
background: linear-gradient(90deg, #edf0f4 0%, #f9fbfd 48%, #edf0f4 100%);
background-size: 220% 100%;
animation: hmall-image-shimmer 1.35s ease-in-out infinite;
}
.hmall-product-image {
position: absolute;
top: 0;
@@ -784,6 +819,12 @@ function showMarketPrice(product: Product): boolean {
width: 100%;
height: 100%;
border-radius: 18rpx 18rpx 0 0;
opacity: 0;
transition: opacity 220ms ease;
}
.hmall-product-image-loaded {
opacity: 1;
}
.hmall-product-image-wrapper-fixed {
@@ -795,6 +836,15 @@ function showMarketPrice(product: Product): boolean {
background: #ffffff;
}
@keyframes hmall-image-shimmer {
0% {
background-position: 200% 0;
}
100% {
background-position: -20% 0;
}
}
.hmall-product-body {
padding: 14rpx 14rpx 16rpx;
background: #ffffff;