348 lines
7.3 KiB
Vue
348 lines
7.3 KiB
Vue
<template>
|
||
<!--
|
||
列表骨架屏:按 type 模拟医生卡/消息行/商品卡/资讯卡/分类侧栏/商品详情,
|
||
放在分包以控制主包体积,主包页通过 componentPlaceholder 异步引用。
|
||
-->
|
||
<view class="list-skeleton" :class="'list-skeleton--' + type">
|
||
<!-- 商品详情:整页一块骨架,不按 rows 循环 -->
|
||
<template v-if="type === 'detail'">
|
||
<view class="sk-detail-hero sk-shimmer"></view>
|
||
<view class="sk-detail-basic">
|
||
<view class="sk-line sk-line--price sk-shimmer"></view>
|
||
<view class="sk-line sk-line--lg sk-shimmer"></view>
|
||
<view class="sk-line sk-line--md sk-shimmer"></view>
|
||
<view class="sk-detail-spec">
|
||
<view class="sk-line sk-line--sm sk-shimmer"></view>
|
||
<view class="sk-line sk-line--sm sk-shimmer"></view>
|
||
</view>
|
||
</view>
|
||
<view class="sk-detail-tip sk-shimmer"></view>
|
||
<!-- 非 H5 :key 仅支持简单变量,禁止 'card-'+i 拼接 -->
|
||
<view
|
||
v-for="i in detailCardCount"
|
||
:key="i"
|
||
class="sk-detail-card"
|
||
>
|
||
<view class="sk-line sk-line--md sk-shimmer"></view>
|
||
<view class="sk-line sk-line--sm sk-shimmer"></view>
|
||
<view class="sk-line sk-line--lg sk-shimmer"></view>
|
||
</view>
|
||
</template>
|
||
<!-- 列表类骨架按 rows 循环 -->
|
||
<template v-else>
|
||
<view
|
||
v-for="i in rowCount"
|
||
:key="i"
|
||
class="sk-row"
|
||
:class="'sk-row--' + type"
|
||
>
|
||
<!-- 医生推荐卡片 -->
|
||
<template v-if="type === 'doctor'">
|
||
<view class="sk-avatar sk-shimmer"></view>
|
||
<view class="sk-body">
|
||
<view class="sk-line sk-line--lg sk-shimmer"></view>
|
||
<view class="sk-line sk-line--md sk-shimmer"></view>
|
||
<view class="sk-line sk-line--sm sk-shimmer"></view>
|
||
</view>
|
||
</template>
|
||
<!-- 消息列表行 -->
|
||
<template v-else-if="type === 'message'">
|
||
<view class="sk-avatar sk-avatar--round sk-shimmer"></view>
|
||
<view class="sk-body">
|
||
<view class="sk-line-row">
|
||
<view class="sk-line sk-line--md sk-shimmer"></view>
|
||
<view class="sk-line sk-line--xs sk-shimmer"></view>
|
||
</view>
|
||
<view class="sk-line sk-line--sm sk-shimmer"></view>
|
||
</view>
|
||
</template>
|
||
<!-- 商品卡片 -->
|
||
<template v-else-if="type === 'product'">
|
||
<view class="sk-prod-img sk-shimmer"></view>
|
||
<view class="sk-body">
|
||
<view class="sk-line sk-line--lg sk-shimmer"></view>
|
||
<view class="sk-line sk-line--sm sk-shimmer"></view>
|
||
<view class="sk-line sk-line--md sk-shimmer"></view>
|
||
</view>
|
||
</template>
|
||
<!-- 资讯文章卡片 -->
|
||
<template v-else-if="type === 'article'">
|
||
<view class="sk-cover sk-shimmer"></view>
|
||
<view class="sk-cover-title sk-shimmer"></view>
|
||
</template>
|
||
<!-- 资讯左侧分类 -->
|
||
<template v-else-if="type === 'category'">
|
||
<view class="sk-cate-item sk-shimmer"></view>
|
||
</template>
|
||
<!-- 金刚区横滑块 -->
|
||
<template v-else-if="type === 'zone'">
|
||
<view class="sk-zone-icon sk-shimmer"></view>
|
||
<view class="sk-line sk-line--xs sk-shimmer"></view>
|
||
</template>
|
||
</view>
|
||
</template>
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
export default {
|
||
name: 'ListSkeleton',
|
||
props: {
|
||
// 骨架样式类型(detail 为商品详情整页骨架)
|
||
type: {
|
||
type: String,
|
||
default: 'message',
|
||
validator(v) {
|
||
return ['doctor', 'message', 'product', 'article', 'category', 'zone', 'detail'].indexOf(v) !== -1
|
||
}
|
||
},
|
||
// 行数(detail 时表示信息卡片块数)
|
||
rows: {
|
||
type: [Number, String],
|
||
default: 3
|
||
}
|
||
},
|
||
computed: {
|
||
rowCount() {
|
||
const n = Number(this.rows)
|
||
return n > 0 ? n : 3
|
||
},
|
||
// 详情骨架信息卡数量,默认 3 块
|
||
detailCardCount() {
|
||
return this.rowCount
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.list-skeleton {
|
||
width: 100%;
|
||
}
|
||
|
||
.sk-shimmer {
|
||
background: linear-gradient(90deg, #ebebeb 25%, #f5f5f5 50%, #ebebeb 75%);
|
||
background-size: 400% 100%;
|
||
animation: sk-shimmer 1.4s ease infinite;
|
||
}
|
||
|
||
@keyframes sk-shimmer {
|
||
0% {
|
||
background-position: 100% 0;
|
||
}
|
||
100% {
|
||
background-position: 0 0;
|
||
}
|
||
}
|
||
|
||
.sk-row--doctor {
|
||
display: flex;
|
||
align-items: flex-start;
|
||
margin: 0 24rpx 20rpx;
|
||
padding: 28rpx;
|
||
background: #fff;
|
||
border-radius: 24rpx;
|
||
|
||
.sk-avatar {
|
||
width: 96rpx;
|
||
height: 96rpx;
|
||
border-radius: 50%;
|
||
flex-shrink: 0;
|
||
margin-right: 20rpx;
|
||
}
|
||
|
||
.sk-body {
|
||
flex: 1;
|
||
}
|
||
}
|
||
|
||
.sk-row--message {
|
||
display: flex;
|
||
align-items: center;
|
||
padding: 32rpx 38rpx;
|
||
background: #fff;
|
||
|
||
.sk-avatar {
|
||
width: 84rpx;
|
||
height: 84rpx;
|
||
border-radius: 50%;
|
||
flex-shrink: 0;
|
||
margin-right: 24rpx;
|
||
}
|
||
|
||
.sk-body {
|
||
flex: 1;
|
||
}
|
||
|
||
.sk-line-row {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 12rpx;
|
||
}
|
||
}
|
||
|
||
.sk-row--product {
|
||
display: flex;
|
||
margin: 0 24rpx 20rpx;
|
||
padding: 20rpx;
|
||
background: #fff;
|
||
border-radius: 16rpx;
|
||
|
||
.sk-prod-img {
|
||
width: 160rpx;
|
||
height: 160rpx;
|
||
border-radius: 12rpx;
|
||
flex-shrink: 0;
|
||
margin-right: 20rpx;
|
||
}
|
||
|
||
.sk-body {
|
||
flex: 1;
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: space-between;
|
||
}
|
||
}
|
||
|
||
.sk-row--article {
|
||
position: relative;
|
||
width: 500rpx;
|
||
height: 246rpx;
|
||
margin: 10rpx auto;
|
||
border-radius: 12rpx;
|
||
overflow: hidden;
|
||
|
||
.sk-cover {
|
||
width: 100%;
|
||
height: 100%;
|
||
border-radius: 12rpx;
|
||
}
|
||
|
||
.sk-cover-title {
|
||
position: absolute;
|
||
left: 0;
|
||
bottom: 0;
|
||
width: 100%;
|
||
height: 92rpx;
|
||
opacity: 0.5;
|
||
}
|
||
}
|
||
|
||
.sk-row--category {
|
||
width: 184rpx;
|
||
height: 100rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
background: #fff;
|
||
|
||
.sk-cate-item {
|
||
width: 100rpx;
|
||
height: 28rpx;
|
||
border-radius: 6rpx;
|
||
}
|
||
}
|
||
|
||
.list-skeleton--zone {
|
||
display: flex;
|
||
flex-wrap: nowrap;
|
||
padding: 16rpx 24rpx;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.sk-row--zone {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
width: 140rpx;
|
||
flex-shrink: 0;
|
||
margin-right: 16rpx;
|
||
|
||
.sk-zone-icon {
|
||
width: 88rpx;
|
||
height: 88rpx;
|
||
border-radius: 20rpx;
|
||
margin-bottom: 12rpx;
|
||
}
|
||
|
||
.sk-line--xs {
|
||
width: 72rpx;
|
||
}
|
||
}
|
||
|
||
.sk-line {
|
||
height: 24rpx;
|
||
border-radius: 6rpx;
|
||
margin-bottom: 16rpx;
|
||
|
||
&:last-child {
|
||
margin-bottom: 0;
|
||
}
|
||
}
|
||
|
||
.sk-line--lg {
|
||
width: 70%;
|
||
}
|
||
|
||
.sk-line--md {
|
||
width: 50%;
|
||
}
|
||
|
||
.sk-line--sm {
|
||
width: 90%;
|
||
}
|
||
|
||
.sk-line--xs {
|
||
width: 80rpx;
|
||
height: 20rpx;
|
||
margin-bottom: 0;
|
||
}
|
||
|
||
.sk-line-row .sk-line--md {
|
||
width: 40%;
|
||
margin-bottom: 0;
|
||
}
|
||
|
||
/* 商品详情骨架:大图 + 基础信息 + 物流条 + 信息卡 */
|
||
.list-skeleton--detail {
|
||
background: #F5F7FA;
|
||
min-height: 100vh;
|
||
padding-bottom: 40rpx;
|
||
}
|
||
|
||
.sk-detail-hero {
|
||
width: 100%;
|
||
height: 750rpx;
|
||
}
|
||
|
||
.sk-detail-basic {
|
||
background: #fff;
|
||
padding: 32rpx 30rpx;
|
||
margin-bottom: 20rpx;
|
||
|
||
.sk-line--price {
|
||
width: 200rpx;
|
||
height: 48rpx;
|
||
margin-bottom: 24rpx;
|
||
}
|
||
}
|
||
|
||
.sk-detail-spec {
|
||
margin-top: 24rpx;
|
||
padding-top: 16rpx;
|
||
}
|
||
|
||
.sk-detail-tip {
|
||
height: 80rpx;
|
||
margin: 0 0 20rpx;
|
||
border-radius: 0;
|
||
}
|
||
|
||
.sk-detail-card {
|
||
background: #fff;
|
||
padding: 30rpx;
|
||
margin-bottom: 20rpx;
|
||
}
|
||
</style>
|