| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188 | <template>	<view class="u-skeleton" :class="[`u-skeleton--${type}`, `u-skeleton--${theme}`]" v-if="loading">		<template v-if="type === 'paragraph'">			<view class="u-skeleton-paragraph">				<view 					class="u-skeleton-paragraph-line" 					v-for="(item, index) in paragraphRows" 					:key="index"					:style="{ width: `${lineWidth}%` }"				></view>			</view>		</template>				<template v-else-if="type === 'card'">			<view class="u-skeleton-card">				<view class="u-skeleton-card-header"></view>				<view class="u-skeleton-card-content">					<view class="u-skeleton-card-row"></view>					<view class="u-skeleton-card-row" :style="{ width: '70%' }"></view>					<view class="u-skeleton-card-row" :style="{ width: '90%' }"></view>				</view>			</view>		</template>				<template v-else-if="type === 'list'">			<view class="u-skeleton-list-item" v-for="i in 3" :key="i">				<view class="u-skeleton-list-avatar"></view>				<view class="u-skeleton-list-content">					<view class="u-skeleton-list-title"></view>					<view class="u-skeleton-list-desc"></view>				</view>			</view>		</template>				<template v-else>			<slot></slot>		</template>	</view></template><script setup>import { computed } from 'vue'const props = defineProps({	loading: {		type: Boolean,		default: true	},	type: {		type: String,		default: 'paragraph',		validator: value => ['paragraph', 'card', 'list', 'custom'].includes(value)	},	rows: {		type: Number,		default: 3	},	theme: {		type: String,		default: 'primary',		validator: value => ['primary', 'dark', 'light'].includes(value)	},	lineWidth: {		type: Number,		default: 100	}})const paragraphRows = computed(() => {	return Array.from({ length: props.rows })})</script><style lang="less">@keyframes u-skeleton-blink {	0% { background-color: #f0f0f0; }	50% { background-color: #e0e0e0; }	100% { background-color: #f0f0f0; }}.u-skeleton {	padding: 16rpx;	border-radius: 8rpx;	overflow: hidden;		&--primary {		background-color: #f7f7f7;	}		&--dark {		background-color: #f0f0f0;	}		&--light {		background-color: #ffffff;	}		&::before {		content: "";		position: absolute;		top: 0;		left: 0;		right: 0;		bottom: 0;		animation: u-skeleton-blink 1.5s infinite ease-in-out;		z-index: 1;	}}.u-skeleton-paragraph {	padding: 20rpx;		&-line {		height: 24rpx;		background-color: #e9e9e9;		margin-bottom: 20rpx;		border-radius: 4rpx;	}}.u-skeleton-card {	padding: 24rpx;	background-color: #fff;	border-radius: 8rpx;	margin-bottom: 16rpx;		&-header {		height: 120rpx;		background-color: #e9e9e9;		border-radius: 8rpx;		margin-bottom: 20rpx;	}		&-content {		padding: 0 10rpx;				&-row {			height: 24rpx;			background-color: #e9e9e9;			border-radius: 4rpx;			margin-bottom: 16rpx;		}	}}.u-skeleton-list {	background-color: #fff;	border-radius: 8rpx;	padding: 16rpx;		&-item {		display: flex;		padding: 20rpx 0;		border-bottom: 1rpx solid #eee;				&:last-child {			border-bottom: none;		}				&-avatar {			width: 64rpx;			height: 64rpx;			background-color: #e9e9e9;			border-radius: 50%;			margin-right: 16rpx;		}				&-content {			flex: 1;						&-title {				width: 40%;				height: 20rpx;				background-color: #e9e9e9;				border-radius: 4rpx;				margin-bottom: 12rpx;			}						&-desc {				width: 70%;				height: 16rpx;				background-color: #e9e9e9;				border-radius: 4rpx;			}		}	}}</style>
 |