index.vue 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. <template>
  2. <view class="u-skeleton" :class="[`u-skeleton--${type}`, `u-skeleton--${theme}`]" v-if="loading">
  3. <template v-if="type === 'paragraph'">
  4. <view class="u-skeleton-paragraph">
  5. <view
  6. class="u-skeleton-paragraph-line"
  7. v-for="(item, index) in paragraphRows"
  8. :key="index"
  9. :style="{ width: `${lineWidth}%` }"
  10. ></view>
  11. </view>
  12. </template>
  13. <template v-else-if="type === 'card'">
  14. <view class="u-skeleton-card">
  15. <view class="u-skeleton-card-header"></view>
  16. <view class="u-skeleton-card-content">
  17. <view class="u-skeleton-card-row"></view>
  18. <view class="u-skeleton-card-row" :style="{ width: '70%' }"></view>
  19. <view class="u-skeleton-card-row" :style="{ width: '90%' }"></view>
  20. </view>
  21. </view>
  22. </template>
  23. <template v-else-if="type === 'list'">
  24. <view class="u-skeleton-list-item" v-for="i in 3" :key="i">
  25. <view class="u-skeleton-list-avatar"></view>
  26. <view class="u-skeleton-list-content">
  27. <view class="u-skeleton-list-title"></view>
  28. <view class="u-skeleton-list-desc"></view>
  29. </view>
  30. </view>
  31. </template>
  32. <template v-else>
  33. <slot></slot>
  34. </template>
  35. </view>
  36. </template>
  37. <script setup>
  38. import { computed } from 'vue'
  39. const props = defineProps({
  40. loading: {
  41. type: Boolean,
  42. default: true
  43. },
  44. type: {
  45. type: String,
  46. default: 'paragraph',
  47. validator: value => ['paragraph', 'card', 'list', 'custom'].includes(value)
  48. },
  49. rows: {
  50. type: Number,
  51. default: 3
  52. },
  53. theme: {
  54. type: String,
  55. default: 'primary',
  56. validator: value => ['primary', 'dark', 'light'].includes(value)
  57. },
  58. lineWidth: {
  59. type: Number,
  60. default: 100
  61. }
  62. })
  63. const paragraphRows = computed(() => {
  64. return Array.from({ length: props.rows })
  65. })
  66. </script>
  67. <style lang="less">
  68. @keyframes u-skeleton-blink {
  69. 0% { background-color: #f0f0f0; }
  70. 50% { background-color: #e0e0e0; }
  71. 100% { background-color: #f0f0f0; }
  72. }
  73. .u-skeleton {
  74. padding: 16rpx;
  75. border-radius: 8rpx;
  76. overflow: hidden;
  77. &--primary {
  78. background-color: #f7f7f7;
  79. }
  80. &--dark {
  81. background-color: #f0f0f0;
  82. }
  83. &--light {
  84. background-color: #ffffff;
  85. }
  86. &::before {
  87. content: "";
  88. position: absolute;
  89. top: 0;
  90. left: 0;
  91. right: 0;
  92. bottom: 0;
  93. animation: u-skeleton-blink 1.5s infinite ease-in-out;
  94. z-index: 1;
  95. }
  96. }
  97. .u-skeleton-paragraph {
  98. padding: 20rpx;
  99. &-line {
  100. height: 24rpx;
  101. background-color: #e9e9e9;
  102. margin-bottom: 20rpx;
  103. border-radius: 4rpx;
  104. }
  105. }
  106. .u-skeleton-card {
  107. padding: 24rpx;
  108. background-color: #fff;
  109. border-radius: 8rpx;
  110. margin-bottom: 16rpx;
  111. &-header {
  112. height: 120rpx;
  113. background-color: #e9e9e9;
  114. border-radius: 8rpx;
  115. margin-bottom: 20rpx;
  116. }
  117. &-content {
  118. padding: 0 10rpx;
  119. &-row {
  120. height: 24rpx;
  121. background-color: #e9e9e9;
  122. border-radius: 4rpx;
  123. margin-bottom: 16rpx;
  124. }
  125. }
  126. }
  127. .u-skeleton-list {
  128. background-color: #fff;
  129. border-radius: 8rpx;
  130. padding: 16rpx;
  131. &-item {
  132. display: flex;
  133. padding: 20rpx 0;
  134. border-bottom: 1rpx solid #eee;
  135. &:last-child {
  136. border-bottom: none;
  137. }
  138. &-avatar {
  139. width: 64rpx;
  140. height: 64rpx;
  141. background-color: #e9e9e9;
  142. border-radius: 50%;
  143. margin-right: 16rpx;
  144. }
  145. &-content {
  146. flex: 1;
  147. &-title {
  148. width: 40%;
  149. height: 20rpx;
  150. background-color: #e9e9e9;
  151. border-radius: 4rpx;
  152. margin-bottom: 12rpx;
  153. }
  154. &-desc {
  155. width: 70%;
  156. height: 16rpx;
  157. background-color: #e9e9e9;
  158. border-radius: 4rpx;
  159. }
  160. }
  161. }
  162. }
  163. </style>