Files
xk-client-wx/subPackages/common/components/ListSkeleton.vue

347 lines
7.3 KiB
Vue
Raw Normal View History

<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>
<view
v-for="i in detailCardCount"
:key="'card-' + 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>