221 lines
4.9 KiB
Vue
221 lines
4.9 KiB
Vue
|
|
<template>
|
||
|
|
<view class="loading-skeleton">
|
||
|
|
<view v-for="n in count" :key="n" class="skeleton-item" :class="type">
|
||
|
|
|
||
|
|
<!-- 列表类型骨架屏 -->
|
||
|
|
<template v-if="type === 'list'">
|
||
|
|
<view class="skeleton-header">
|
||
|
|
<view class="skeleton-avatar"></view>
|
||
|
|
<view class="skeleton-content">
|
||
|
|
<view class="skeleton-line long"></view>
|
||
|
|
<view class="skeleton-line short"></view>
|
||
|
|
</view>
|
||
|
|
<view class="skeleton-badge"></view>
|
||
|
|
</view>
|
||
|
|
<view class="skeleton-body">
|
||
|
|
<view class="skeleton-line"></view>
|
||
|
|
<view class="skeleton-line"></view>
|
||
|
|
<view class="skeleton-line medium"></view>
|
||
|
|
</view>
|
||
|
|
<view class="skeleton-footer">
|
||
|
|
<view class="skeleton-button"></view>
|
||
|
|
<view class="skeleton-button"></view>
|
||
|
|
<view class="skeleton-button"></view>
|
||
|
|
</view>
|
||
|
|
</template>
|
||
|
|
|
||
|
|
<!-- 卡片类型骨架屏 -->
|
||
|
|
<template v-else-if="type === 'card'">
|
||
|
|
<view class="skeleton-card-header">
|
||
|
|
<view class="skeleton-circle"></view>
|
||
|
|
<view class="skeleton-lines">
|
||
|
|
<view class="skeleton-line"></view>
|
||
|
|
<view class="skeleton-line short"></view>
|
||
|
|
</view>
|
||
|
|
</view>
|
||
|
|
<view class="skeleton-card-body">
|
||
|
|
<view class="skeleton-line"></view>
|
||
|
|
<view class="skeleton-line"></view>
|
||
|
|
</view>
|
||
|
|
</template>
|
||
|
|
|
||
|
|
<!-- 简单类型骨架屏 -->
|
||
|
|
<template v-else>
|
||
|
|
<view class="skeleton-line"></view>
|
||
|
|
<view class="skeleton-line medium"></view>
|
||
|
|
<view class="skeleton-line short"></view>
|
||
|
|
</template>
|
||
|
|
</view>
|
||
|
|
</view>
|
||
|
|
</template>
|
||
|
|
|
||
|
|
<script>
|
||
|
|
export default {
|
||
|
|
name: 'LoadingSkeleton',
|
||
|
|
props: {
|
||
|
|
type: {
|
||
|
|
type: String,
|
||
|
|
default: 'simple', // simple, list, card
|
||
|
|
validator: value => ['simple', 'list', 'card'].includes(value)
|
||
|
|
},
|
||
|
|
count: {
|
||
|
|
type: Number,
|
||
|
|
default: 3
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
</script>
|
||
|
|
|
||
|
|
<style lang="scss" scoped>
|
||
|
|
.loading-skeleton {
|
||
|
|
padding: 20rpx;
|
||
|
|
}
|
||
|
|
|
||
|
|
.skeleton-item {
|
||
|
|
background: #fff;
|
||
|
|
border-radius: 16rpx;
|
||
|
|
padding: 30rpx;
|
||
|
|
margin-bottom: 20rpx;
|
||
|
|
|
||
|
|
&.list {
|
||
|
|
.skeleton-header {
|
||
|
|
display: flex;
|
||
|
|
align-items: center;
|
||
|
|
margin-bottom: 20rpx;
|
||
|
|
|
||
|
|
.skeleton-avatar {
|
||
|
|
width: 60rpx;
|
||
|
|
height: 60rpx;
|
||
|
|
border-radius: 50%;
|
||
|
|
background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
|
||
|
|
background-size: 200% 100%;
|
||
|
|
animation: loading 1.5s infinite;
|
||
|
|
margin-right: 20rpx;
|
||
|
|
}
|
||
|
|
|
||
|
|
.skeleton-content {
|
||
|
|
flex: 1;
|
||
|
|
|
||
|
|
.skeleton-line {
|
||
|
|
&.long {
|
||
|
|
width: 80%;
|
||
|
|
}
|
||
|
|
|
||
|
|
&.short {
|
||
|
|
width: 50%;
|
||
|
|
margin-top: 8rpx;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
.skeleton-badge {
|
||
|
|
width: 60rpx;
|
||
|
|
height: 30rpx;
|
||
|
|
border-radius: 15rpx;
|
||
|
|
background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
|
||
|
|
background-size: 200% 100%;
|
||
|
|
animation: loading 1.5s infinite;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
.skeleton-body {
|
||
|
|
margin-bottom: 20rpx;
|
||
|
|
|
||
|
|
.skeleton-line {
|
||
|
|
margin-bottom: 12rpx;
|
||
|
|
|
||
|
|
&:last-child {
|
||
|
|
margin-bottom: 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
&.medium {
|
||
|
|
width: 70%;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
.skeleton-footer {
|
||
|
|
display: flex;
|
||
|
|
gap: 12rpx;
|
||
|
|
|
||
|
|
.skeleton-button {
|
||
|
|
flex: 1;
|
||
|
|
height: 60rpx;
|
||
|
|
border-radius: 30rpx;
|
||
|
|
background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
|
||
|
|
background-size: 200% 100%;
|
||
|
|
animation: loading 1.5s infinite;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
&.card {
|
||
|
|
.skeleton-card-header {
|
||
|
|
display: flex;
|
||
|
|
align-items: center;
|
||
|
|
margin-bottom: 20rpx;
|
||
|
|
|
||
|
|
.skeleton-circle {
|
||
|
|
width: 60rpx;
|
||
|
|
height: 60rpx;
|
||
|
|
border-radius: 50%;
|
||
|
|
background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
|
||
|
|
background-size: 200% 100%;
|
||
|
|
animation: loading 1.5s infinite;
|
||
|
|
margin-right: 20rpx;
|
||
|
|
}
|
||
|
|
|
||
|
|
.skeleton-lines {
|
||
|
|
flex: 1;
|
||
|
|
|
||
|
|
.skeleton-line {
|
||
|
|
margin-bottom: 8rpx;
|
||
|
|
|
||
|
|
&.short {
|
||
|
|
width: 60%;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
.skeleton-card-body {
|
||
|
|
.skeleton-line {
|
||
|
|
margin-bottom: 12rpx;
|
||
|
|
|
||
|
|
&:last-child {
|
||
|
|
margin-bottom: 0;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
.skeleton-line {
|
||
|
|
height: 24rpx;
|
||
|
|
background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
|
||
|
|
background-size: 200% 100%;
|
||
|
|
animation: loading 1.5s infinite;
|
||
|
|
border-radius: 4rpx;
|
||
|
|
|
||
|
|
&.short {
|
||
|
|
width: 40%;
|
||
|
|
}
|
||
|
|
|
||
|
|
&.medium {
|
||
|
|
width: 60%;
|
||
|
|
}
|
||
|
|
|
||
|
|
&.long {
|
||
|
|
width: 80%;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
@keyframes loading {
|
||
|
|
0% {
|
||
|
|
background-position: 200% 0;
|
||
|
|
}
|
||
|
|
100% {
|
||
|
|
background-position: -200% 0;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
</style>
|