feat(AdPage): 添加手动刷新功能和加载超时处理
添加手动刷新UI组件和加载超时处理逻辑,当图片加载失败或超时时显示刷新选项 支持自动重试机制,超过最大重试次数后显示手动刷新按钮
Showing
2 changed files
with
189 additions
and
3 deletions
| ... | @@ -148,6 +148,86 @@ | ... | @@ -148,6 +148,86 @@ |
| 148 | } | 148 | } |
| 149 | } | 149 | } |
| 150 | 150 | ||
| 151 | + // 手动刷新样式 | ||
| 152 | + .manual-refresh-container { | ||
| 153 | + position: absolute; | ||
| 154 | + top: 0; | ||
| 155 | + left: 0; | ||
| 156 | + width: 100%; | ||
| 157 | + height: 100vh; | ||
| 158 | + display: flex; | ||
| 159 | + justify-content: center; | ||
| 160 | + align-items: center; | ||
| 161 | + background-color: #f8f9fa; | ||
| 162 | + z-index: 2; | ||
| 163 | + } | ||
| 164 | + | ||
| 165 | + .refresh-content { | ||
| 166 | + text-align: center; | ||
| 167 | + padding: 60rpx 40rpx; | ||
| 168 | + background: white; | ||
| 169 | + border-radius: 16rpx; | ||
| 170 | + box-shadow: 0 8rpx 32rpx rgba(0, 0, 0, 0.1); | ||
| 171 | + margin: 0 40rpx; | ||
| 172 | + max-width: 600rpx; | ||
| 173 | + } | ||
| 174 | + | ||
| 175 | + .refresh-icon { | ||
| 176 | + font-size: 80rpx; | ||
| 177 | + margin-bottom: 24rpx; | ||
| 178 | + } | ||
| 179 | + | ||
| 180 | + .refresh-title { | ||
| 181 | + font-size: 36rpx; | ||
| 182 | + font-weight: 600; | ||
| 183 | + color: #333; | ||
| 184 | + margin-bottom: 16rpx; | ||
| 185 | + } | ||
| 186 | + | ||
| 187 | + .refresh-subtitle { | ||
| 188 | + font-size: 28rpx; | ||
| 189 | + color: #666; | ||
| 190 | + margin-bottom: 40rpx; | ||
| 191 | + line-height: 1.5; | ||
| 192 | + } | ||
| 193 | + | ||
| 194 | + .refresh-buttons { | ||
| 195 | + display: flex; | ||
| 196 | + gap: 20rpx; | ||
| 197 | + justify-content: center; | ||
| 198 | + } | ||
| 199 | + | ||
| 200 | + .refresh-button { | ||
| 201 | + flex: 1; | ||
| 202 | + padding: 24rpx 32rpx; | ||
| 203 | + background: linear-gradient(135deg, #007aff, #0056cc); | ||
| 204 | + color: white; | ||
| 205 | + border-radius: 12rpx; | ||
| 206 | + font-size: 32rpx; | ||
| 207 | + font-weight: 600; | ||
| 208 | + text-align: center; | ||
| 209 | + | ||
| 210 | + &:active { | ||
| 211 | + background: linear-gradient(135deg, #0056cc, #003d99); | ||
| 212 | + transform: scale(0.98); | ||
| 213 | + } | ||
| 214 | + } | ||
| 215 | + | ||
| 216 | + .retry-button-small { | ||
| 217 | + flex: 1; | ||
| 218 | + padding: 24rpx 32rpx; | ||
| 219 | + background: #f0f0f0; | ||
| 220 | + color: #666; | ||
| 221 | + border-radius: 12rpx; | ||
| 222 | + font-size: 32rpx; | ||
| 223 | + text-align: center; | ||
| 224 | + | ||
| 225 | + &:active { | ||
| 226 | + background: #e0e0e0; | ||
| 227 | + transform: scale(0.98); | ||
| 228 | + } | ||
| 229 | + } | ||
| 230 | + | ||
| 151 | // 错误提示样式 | 231 | // 错误提示样式 |
| 152 | .error-container { | 232 | .error-container { |
| 153 | position: absolute; | 233 | position: absolute; | ... | ... |
| ... | @@ -12,12 +12,27 @@ | ... | @@ -12,12 +12,27 @@ |
| 12 | <view class="skeleton-image"> | 12 | <view class="skeleton-image"> |
| 13 | <view class="skeleton-shimmer"></view> | 13 | <view class="skeleton-shimmer"></view> |
| 14 | <view class="skeleton-text"> | 14 | <view class="skeleton-text"> |
| 15 | - <view class="loading-title">正在加载精彩内容</view> | 15 | + <view class="loading-title"> |
| 16 | + {{ retryCount > 0 ? `正在重试加载 (${retryCount}/${maxRetries})` : '正在加载精彩内容' }} | ||
| 17 | + </view> | ||
| 16 | <view class="loading-subtitle">请稍候...</view> | 18 | <view class="loading-subtitle">请稍候...</view> |
| 17 | </view> | 19 | </view> |
| 18 | </view> | 20 | </view> |
| 19 | </view> | 21 | </view> |
| 20 | 22 | ||
| 23 | + <!-- 手动刷新提示 --> | ||
| 24 | + <view v-if="showManualRefresh" class="manual-refresh-container"> | ||
| 25 | + <view class="refresh-content"> | ||
| 26 | + <view class="refresh-icon">⚠️</view> | ||
| 27 | + <view class="refresh-title">加载遇到问题</view> | ||
| 28 | + <view class="refresh-subtitle">网络可能不稳定,请尝试刷新页面</view> | ||
| 29 | + <view class="refresh-buttons"> | ||
| 30 | + <view class="refresh-button" @tap="manualRefresh">刷新页面</view> | ||
| 31 | + <view class="retry-button-small" @tap="retryLoadImage">重试</view> | ||
| 32 | + </view> | ||
| 33 | + </view> | ||
| 34 | + </view> | ||
| 35 | + | ||
| 21 | <!-- 背景图片 --> | 36 | <!-- 背景图片 --> |
| 22 | <image | 37 | <image |
| 23 | v-show="!imageLoading && !imageLoadError" | 38 | v-show="!imageLoading && !imageLoadError" |
| ... | @@ -66,6 +81,11 @@ const loading = ref(true); | ... | @@ -66,6 +81,11 @@ const loading = ref(true); |
| 66 | const hasFamily = ref(false); | 81 | const hasFamily = ref(false); |
| 67 | const imageLoading = ref(true); // 图片加载状态 | 82 | const imageLoading = ref(true); // 图片加载状态 |
| 68 | const imageLoadError = ref(false); // 图片加载错误状态 | 83 | const imageLoadError = ref(false); // 图片加载错误状态 |
| 84 | +const loadingTimeout = ref(null); // 加载超时定时器 | ||
| 85 | +const retryCount = ref(0); // 重试次数 | ||
| 86 | +const maxRetries = ref(3); // 最大重试次数 | ||
| 87 | +const isTimeout = ref(false); // 是否超时 | ||
| 88 | +const showManualRefresh = ref(false); // 是否显示手动刷新按钮 | ||
| 69 | 89 | ||
| 70 | /** | 90 | /** |
| 71 | * 获取广告配置 | 91 | * 获取广告配置 |
| ... | @@ -126,16 +146,22 @@ const handleImageClick = () => { | ... | @@ -126,16 +146,22 @@ const handleImageClick = () => { |
| 126 | * 图片加载完成处理 | 146 | * 图片加载完成处理 |
| 127 | */ | 147 | */ |
| 128 | const handleImageLoad = () => { | 148 | const handleImageLoad = () => { |
| 149 | + clearLoadingTimeout(); | ||
| 129 | imageLoading.value = false; | 150 | imageLoading.value = false; |
| 130 | imageLoadError.value = false; | 151 | imageLoadError.value = false; |
| 152 | + isTimeout.value = false; | ||
| 153 | + showManualRefresh.value = false; | ||
| 154 | + retryCount.value = 0; | ||
| 131 | }; | 155 | }; |
| 132 | 156 | ||
| 133 | /** | 157 | /** |
| 134 | * 图片加载失败处理 | 158 | * 图片加载失败处理 |
| 135 | */ | 159 | */ |
| 136 | const handleImageError = () => { | 160 | const handleImageError = () => { |
| 161 | + clearLoadingTimeout(); | ||
| 137 | imageLoading.value = false; | 162 | imageLoading.value = false; |
| 138 | imageLoadError.value = true; | 163 | imageLoadError.value = true; |
| 164 | + handleLoadingTimeout(); | ||
| 139 | }; | 165 | }; |
| 140 | 166 | ||
| 141 | /** | 167 | /** |
| ... | @@ -144,15 +170,86 @@ const handleImageError = () => { | ... | @@ -144,15 +170,86 @@ const handleImageError = () => { |
| 144 | const retryLoadImage = () => { | 170 | const retryLoadImage = () => { |
| 145 | imageLoading.value = true; | 171 | imageLoading.value = true; |
| 146 | imageLoadError.value = false; | 172 | imageLoadError.value = false; |
| 173 | + isTimeout.value = false; | ||
| 174 | + showManualRefresh.value = false; | ||
| 175 | + | ||
| 176 | + // 开始超时检测 | ||
| 177 | + startLoadingTimeout(); | ||
| 178 | + | ||
| 147 | // 重新获取广告配置 | 179 | // 重新获取广告配置 |
| 148 | fetchAdConfig(); | 180 | fetchAdConfig(); |
| 149 | }; | 181 | }; |
| 150 | 182 | ||
| 151 | /** | 183 | /** |
| 152 | - * 页面初始化 | 184 | + * 手动刷新页面 |
| 185 | + */ | ||
| 186 | +const manualRefresh = () => { | ||
| 187 | + // 重置所有状态 | ||
| 188 | + retryCount.value = 0; | ||
| 189 | + imageLoading.value = true; | ||
| 190 | + imageLoadError.value = false; | ||
| 191 | + isTimeout.value = false; | ||
| 192 | + showManualRefresh.value = false; | ||
| 193 | + loading.value = true; | ||
| 194 | + | ||
| 195 | + // 重新初始化页面 | ||
| 196 | + initializePage(); | ||
| 197 | +}; | ||
| 198 | + | ||
| 199 | +/** | ||
| 200 | + * 开始加载超时检测 | ||
| 201 | + */ | ||
| 202 | +const startLoadingTimeout = () => { | ||
| 203 | + // 清除之前的定时器 | ||
| 204 | + if (loadingTimeout.value) { | ||
| 205 | + clearTimeout(loadingTimeout.value); | ||
| 206 | + } | ||
| 207 | + | ||
| 208 | + // 设置10秒超时 | ||
| 209 | + loadingTimeout.value = setTimeout(() => { | ||
| 210 | + if (imageLoading.value) { | ||
| 211 | + console.warn('图片加载超时'); | ||
| 212 | + isTimeout.value = true; | ||
| 213 | + handleLoadingTimeout(); | ||
| 214 | + } | ||
| 215 | + }, 10000); | ||
| 216 | +}; | ||
| 217 | + | ||
| 218 | +/** | ||
| 219 | + * 处理加载超时 | ||
| 153 | */ | 220 | */ |
| 154 | -onMounted(async () => { | 221 | +const handleLoadingTimeout = () => { |
| 222 | + if (retryCount.value < maxRetries.value) { | ||
| 223 | + // 自动重试 | ||
| 224 | + retryCount.value++; | ||
| 225 | + console.log(`第${retryCount.value}次自动重试加载`); | ||
| 226 | + retryLoadImage(); | ||
| 227 | + } else { | ||
| 228 | + // 超过最大重试次数,显示手动刷新选项 | ||
| 229 | + imageLoading.value = false; | ||
| 230 | + showManualRefresh.value = true; | ||
| 231 | + console.error('图片加载失败,已达到最大重试次数'); | ||
| 232 | + } | ||
| 233 | +}; | ||
| 234 | + | ||
| 235 | +/** | ||
| 236 | + * 清除加载超时定时器 | ||
| 237 | + */ | ||
| 238 | +const clearLoadingTimeout = () => { | ||
| 239 | + if (loadingTimeout.value) { | ||
| 240 | + clearTimeout(loadingTimeout.value); | ||
| 241 | + loadingTimeout.value = null; | ||
| 242 | + } | ||
| 243 | +}; | ||
| 244 | + | ||
| 245 | +/** | ||
| 246 | + * 页面初始化逻辑 | ||
| 247 | + */ | ||
| 248 | +const initializePage = async () => { | ||
| 155 | try { | 249 | try { |
| 250 | + // 开始超时检测 | ||
| 251 | + startLoadingTimeout(); | ||
| 252 | + | ||
| 156 | // 执行静默授权 | 253 | // 执行静默授权 |
| 157 | await performSilentAuth(); | 254 | await performSilentAuth(); |
| 158 | 255 | ||
| ... | @@ -163,9 +260,18 @@ onMounted(async () => { | ... | @@ -163,9 +260,18 @@ onMounted(async () => { |
| 163 | ]); | 260 | ]); |
| 164 | } catch (error) { | 261 | } catch (error) { |
| 165 | console.error('页面初始化失败:', error); | 262 | console.error('页面初始化失败:', error); |
| 263 | + // 初始化失败时也要处理超时 | ||
| 264 | + handleLoadingTimeout(); | ||
| 166 | } finally { | 265 | } finally { |
| 167 | loading.value = false; | 266 | loading.value = false; |
| 168 | } | 267 | } |
| 268 | +}; | ||
| 269 | + | ||
| 270 | +/** | ||
| 271 | + * 页面初始化 | ||
| 272 | + */ | ||
| 273 | +onMounted(() => { | ||
| 274 | + initializePage(); | ||
| 169 | }); | 275 | }); |
| 170 | 276 | ||
| 171 | /** | 277 | /** | ... | ... |
-
Please register or login to post a comment