feat(AdPage): 添加图片加载状态管理和骨架屏效果
添加图片加载状态管理,包括加载中、加载成功和失败状态 实现骨架屏效果提升用户体验 添加图片加载失败时的重试功能 更新分享标题文案
Showing
2 changed files
with
171 additions
and
3 deletions
| ... | @@ -60,4 +60,124 @@ | ... | @@ -60,4 +60,124 @@ |
| 60 | font-size: 32rpx; | 60 | font-size: 32rpx; |
| 61 | color: #666; | 61 | color: #666; |
| 62 | font-weight: 500; | 62 | font-weight: 500; |
| 63 | -} | 63 | + } |
| 64 | + | ||
| 65 | + // 骨架屏样式 | ||
| 66 | + .skeleton-container { | ||
| 67 | + position: absolute; | ||
| 68 | + top: 0; | ||
| 69 | + left: 0; | ||
| 70 | + width: 100%; | ||
| 71 | + height: 100vh; | ||
| 72 | + z-index: 1; | ||
| 73 | + } | ||
| 74 | + | ||
| 75 | + .skeleton-image { | ||
| 76 | + width: 100%; | ||
| 77 | + height: 100%; | ||
| 78 | + background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%); | ||
| 79 | + background-size: 200% 100%; | ||
| 80 | + animation: skeleton-loading 1.5s infinite; | ||
| 81 | + position: relative; | ||
| 82 | + overflow: hidden; | ||
| 83 | + display: flex; | ||
| 84 | + flex-direction: column; | ||
| 85 | + justify-content: center; | ||
| 86 | + align-items: center; | ||
| 87 | + } | ||
| 88 | + | ||
| 89 | + .skeleton-shimmer { | ||
| 90 | + position: absolute; | ||
| 91 | + top: 0; | ||
| 92 | + left: -100%; | ||
| 93 | + width: 100%; | ||
| 94 | + height: 100%; | ||
| 95 | + background: linear-gradient( | ||
| 96 | + 90deg, | ||
| 97 | + transparent, | ||
| 98 | + rgba(255, 255, 255, 0.4), | ||
| 99 | + transparent | ||
| 100 | + ); | ||
| 101 | + animation: shimmer 1.5s infinite; | ||
| 102 | + } | ||
| 103 | + | ||
| 104 | + .skeleton-text { | ||
| 105 | + position: relative; | ||
| 106 | + z-index: 2; | ||
| 107 | + text-align: center; | ||
| 108 | + color: #666; | ||
| 109 | + } | ||
| 110 | + | ||
| 111 | + .loading-title { | ||
| 112 | + font-size: 36rpx; | ||
| 113 | + font-weight: 600; | ||
| 114 | + margin-bottom: 16rpx; | ||
| 115 | + opacity: 0.8; | ||
| 116 | + } | ||
| 117 | + | ||
| 118 | + .loading-subtitle { | ||
| 119 | + font-size: 28rpx; | ||
| 120 | + opacity: 0.6; | ||
| 121 | + animation: pulse 2s infinite; | ||
| 122 | + } | ||
| 123 | + | ||
| 124 | + @keyframes skeleton-loading { | ||
| 125 | + 0% { | ||
| 126 | + background-position: 200% 0; | ||
| 127 | + } | ||
| 128 | + 100% { | ||
| 129 | + background-position: -200% 0; | ||
| 130 | + } | ||
| 131 | + } | ||
| 132 | + | ||
| 133 | + @keyframes shimmer { | ||
| 134 | + 0% { | ||
| 135 | + left: -100%; | ||
| 136 | + } | ||
| 137 | + 100% { | ||
| 138 | + left: 100%; | ||
| 139 | + } | ||
| 140 | + } | ||
| 141 | + | ||
| 142 | + @keyframes pulse { | ||
| 143 | + 0%, 100% { | ||
| 144 | + opacity: 0.6; | ||
| 145 | + } | ||
| 146 | + 50% { | ||
| 147 | + opacity: 0.3; | ||
| 148 | + } | ||
| 149 | + } | ||
| 150 | + | ||
| 151 | + // 错误提示样式 | ||
| 152 | + .error-container { | ||
| 153 | + position: absolute; | ||
| 154 | + top: 0; | ||
| 155 | + left: 0; | ||
| 156 | + width: 100%; | ||
| 157 | + height: 100vh; | ||
| 158 | + display: flex; | ||
| 159 | + flex-direction: column; | ||
| 160 | + justify-content: center; | ||
| 161 | + align-items: center; | ||
| 162 | + background-color: #f5f5f5; | ||
| 163 | + z-index: 1; | ||
| 164 | + } | ||
| 165 | + | ||
| 166 | + .error-text { | ||
| 167 | + font-size: 32rpx; | ||
| 168 | + color: #999; | ||
| 169 | + margin-bottom: 40rpx; | ||
| 170 | + } | ||
| 171 | + | ||
| 172 | + .retry-button { | ||
| 173 | + padding: 20rpx 40rpx; | ||
| 174 | + background-color: #007aff; | ||
| 175 | + color: white; | ||
| 176 | + border-radius: 8rpx; | ||
| 177 | + font-size: 28rpx; | ||
| 178 | + cursor: pointer; | ||
| 179 | + | ||
| 180 | + &:active { | ||
| 181 | + background-color: #0056cc; | ||
| 182 | + } | ||
| 183 | + } | ... | ... |
| 1 | <!-- | 1 | <!-- |
| 2 | * @Date: 2022-09-19 14:11:06 | 2 | * @Date: 2022-09-19 14:11:06 |
| 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com | 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com |
| 4 | - * @LastEditTime: 2025-09-19 20:19:12 | 4 | + * @LastEditTime: 2025-09-25 21:22:49 |
| 5 | * @FilePath: /lls_program/src/pages/AdPage/index.vue | 5 | * @FilePath: /lls_program/src/pages/AdPage/index.vue |
| 6 | * @Description: 广告页面 | 6 | * @Description: 广告页面 |
| 7 | --> | 7 | --> |
| 8 | <template> | 8 | <template> |
| 9 | <view class="ad-page"> | 9 | <view class="ad-page"> |
| 10 | + <!-- 骨架屏 --> | ||
| 11 | + <view v-if="imageLoading" class="skeleton-container"> | ||
| 12 | + <view class="skeleton-image"> | ||
| 13 | + <view class="skeleton-shimmer"></view> | ||
| 14 | + <view class="skeleton-text"> | ||
| 15 | + <view class="loading-title">正在加载精彩内容</view> | ||
| 16 | + <view class="loading-subtitle">请稍候...</view> | ||
| 17 | + </view> | ||
| 18 | + </view> | ||
| 19 | + </view> | ||
| 20 | + | ||
| 10 | <!-- 背景图片 --> | 21 | <!-- 背景图片 --> |
| 11 | <image | 22 | <image |
| 23 | + v-show="!imageLoading && !imageLoadError" | ||
| 12 | :src="adImageUrl" | 24 | :src="adImageUrl" |
| 13 | class="ad-background" | 25 | class="ad-background" |
| 14 | mode="scaleToFill" | 26 | mode="scaleToFill" |
| 15 | @tap="handleImageClick" | 27 | @tap="handleImageClick" |
| 28 | + @load="handleImageLoad" | ||
| 29 | + @error="handleImageError" | ||
| 16 | /> | 30 | /> |
| 17 | 31 | ||
| 32 | + <!-- 图片加载失败提示 --> | ||
| 33 | + <view v-if="imageLoadError" class="error-container"> | ||
| 34 | + <view class="error-text">图片加载失败</view> | ||
| 35 | + <view class="retry-button" @tap="retryLoadImage">重试</view> | ||
| 36 | + </view> | ||
| 37 | + | ||
| 18 | <!-- 右上角分享按钮 --> | 38 | <!-- 右上角分享按钮 --> |
| 19 | <view class="share-button-container" @tap.stop> | 39 | <view class="share-button-container" @tap.stop> |
| 20 | <button | 40 | <button |
| ... | @@ -44,6 +64,8 @@ import "./index.less"; | ... | @@ -44,6 +64,8 @@ import "./index.less"; |
| 44 | const adImageUrl = ref(''); | 64 | const adImageUrl = ref(''); |
| 45 | const loading = ref(true); | 65 | const loading = ref(true); |
| 46 | const hasFamily = ref(false); | 66 | const hasFamily = ref(false); |
| 67 | +const imageLoading = ref(true); // 图片加载状态 | ||
| 68 | +const imageLoadError = ref(false); // 图片加载错误状态 | ||
| 47 | 69 | ||
| 48 | /** | 70 | /** |
| 49 | * 获取广告配置 | 71 | * 获取广告配置 |
| ... | @@ -101,6 +123,32 @@ const handleImageClick = () => { | ... | @@ -101,6 +123,32 @@ const handleImageClick = () => { |
| 101 | }; | 123 | }; |
| 102 | 124 | ||
| 103 | /** | 125 | /** |
| 126 | + * 图片加载完成处理 | ||
| 127 | + */ | ||
| 128 | +const handleImageLoad = () => { | ||
| 129 | + imageLoading.value = false; | ||
| 130 | + imageLoadError.value = false; | ||
| 131 | +}; | ||
| 132 | + | ||
| 133 | +/** | ||
| 134 | + * 图片加载失败处理 | ||
| 135 | + */ | ||
| 136 | +const handleImageError = () => { | ||
| 137 | + imageLoading.value = false; | ||
| 138 | + imageLoadError.value = true; | ||
| 139 | +}; | ||
| 140 | + | ||
| 141 | +/** | ||
| 142 | + * 重试加载图片 | ||
| 143 | + */ | ||
| 144 | +const retryLoadImage = () => { | ||
| 145 | + imageLoading.value = true; | ||
| 146 | + imageLoadError.value = false; | ||
| 147 | + // 重新获取广告配置 | ||
| 148 | + fetchAdConfig(); | ||
| 149 | +}; | ||
| 150 | + | ||
| 151 | +/** | ||
| 104 | * 页面初始化 | 152 | * 页面初始化 |
| 105 | */ | 153 | */ |
| 106 | onMounted(async () => { | 154 | onMounted(async () => { |
| ... | @@ -126,7 +174,7 @@ onMounted(async () => { | ... | @@ -126,7 +174,7 @@ onMounted(async () => { |
| 126 | */ | 174 | */ |
| 127 | const onShareAppMessage = (res) => { | 175 | const onShareAppMessage = (res) => { |
| 128 | const shareData = { | 176 | const shareData = { |
| 129 | - title: '和家人一起走路、一起打卡、一起兑换', | 177 | + title: '和家长一起走路、一起打卡、一起兑换', |
| 130 | path: `/pages/AdPage/index`, | 178 | path: `/pages/AdPage/index`, |
| 131 | imageUrl: '' | 179 | imageUrl: '' |
| 132 | }; | 180 | }; | ... | ... |
-
Please register or login to post a comment