feat(goods-detail): 商品详情页接入骨架屏

- 新增 7 段占位(banner / 价格 / 标题 / 规格 / 商家 / 详情 / 推荐)
  配合底部 action 占位(fixed bottom,2 个 icon + 2 个 btn),
  避免首屏出现"空白 → 真实内容"的跳变
- detail.vue 透传 loading 状态;骨架屏在加载态、未拿到 goods_id 时
  持续显示(hasRealData computed),网络断开时不切到空详情
- loading-cover 改为 initShow=false;骨架屏接管首屏视觉
- 骨架屏最短展示 300ms,保证数据快时用户也能感知到占位

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-07-24 18:10:31 +08:00
parent dac27ca43b
commit 9eb42c4a0c
3 changed files with 248 additions and 18 deletions

View File

@@ -4,7 +4,10 @@ export default {
goodsRoute: '/pages_goods/detail',
couponList: [], //优惠券列表
couponBtnSwitch: false, //获取优惠券防止重复提交
// 商品详情骨架屏:true 显示骨架,false 显示真实内容
// 首屏进入即为 true,商品详情数据首屏渲染完成后(与 loading-cover 同时机)置 false
loading: true,
posterApi: '/api/goods/poster',
//满减活动
@@ -103,13 +106,12 @@ export default {
await this.getGoodsSkuDetail();
} catch (e) {
console.error('getGoodsSkuDetail failed:', e);
// 即使失败也要保证 loading-cover 关闭, 避免空白屏
setTimeout(() => {
try {
const ref = this.$refs && this.$refs.loadingCover;
if (ref && typeof ref.hide === 'function') ref.hide();
} catch (e2) { /* swallow */ }
}, 0);
// 失败时不关闭骨架屏, 由视图层 hasRealData(goods_id>0) 自动保留占位,
// 避免错误地把页面切到"空详情"状态
uni.showToast({
title: this.$lang ? this.$lang('common.networkError') : '网络异常,请重试',
icon: 'none'
});
}
},
onHide() {
@@ -203,14 +205,12 @@ export default {
await this.$nextTick();
// #endif
// 关闭 loading-cover 提前到 nextTick 后, 让用户看到首屏内容
// 用 setTimeout 0 推迟到下一个 task, 避开当前 sync 阶段 vendor 报错导致 hide 失败
// 关闭商品详情骨架屏: 在真实数据首屏渲染完成后再隐藏骨架屏,
// 用户会看到骨架屏 → 真实详情 的清晰过渡
// 最短展示 300ms: 数据请求很快时也保留足够感知时间, 否则一闪而过用户根本看不到骨架屏
setTimeout(() => {
try {
const ref = this.$refs && this.$refs.loadingCover;
if (ref && typeof ref.hide === 'function') ref.hide();
} catch (e) { /* swallow */ }
}, 0);
this.loading = false;
}, 300);
// 处理商品数据 (同步字段变换 + 后台并发 getGoodsForm)
await this.handleGoodsSkuData();