fs-avatar.vue 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. <template>
  2. <view
  3. class="fs-avatar"
  4. :class="[
  5. shape,
  6. {
  7. radius,
  8. fixed
  9. }
  10. ]"
  11. :style="{
  12. width: width || size,
  13. height: height || size,
  14. right: fixed ? right : 0,
  15. bottom: fixed ? bottom : 0,
  16. border: borderStyle,
  17. 'margin-left': avatarGroup.margin + 'rpx'
  18. }"
  19. @click="handleClick"
  20. >
  21. <image
  22. class="fs-avatar-img"
  23. :src="errImg"
  24. v-if="errImg"
  25. :lazy-load="lazyLoad"
  26. :mode="imageMode"
  27. @click="handlePreview"
  28. />
  29. <image
  30. class="fs-avatar-img"
  31. :src="src"
  32. v-if="src"
  33. :lazy-load="lazyLoad"
  34. :mode="imageMode"
  35. @click="handlePreview"
  36. @error="handleError"
  37. />
  38. <view v-else class="fs-avatar-slot" :class="['bg-' + bgColorType]" :style="{ backgroundColor: bgColor }">
  39. <slot></slot>
  40. </view>
  41. </view>
  42. </template>
  43. <script>
  44. /**
  45. * 头像组件
  46. * @description 头像组件
  47. * @property {String} src 头像地址
  48. * @property {String} shape = [circle | square] 头像类型
  49. * @property {String} size 头像大小
  50. * @property {String} width 头像宽
  51. * @property {String} height 头像高
  52. * @property {String} bgColor 背景颜色
  53. * @property {String} bgColorType = [primary | danger | warning | info | success] 背景颜色类型
  54. * @property {Boolean} border 是否展示边框
  55. * @property {String} borderWidth 边框宽
  56. * @property {String} borderColor 边框颜色
  57. * @property {Boolean} lazyLoad 是否懒加载
  58. * @property {String} imageMode 图片模式
  59. * @property {Boolean} preview 是否预览图片
  60. * @property {Boolean} radius 是否圆角
  61. * @property {String} link 跳转地址
  62. * @property {String} linkType 跳转类型
  63. * @property {Boolean} fixed 是否悬浮
  64. * @property {String} right 悬浮右边距(仅在fixed为true时有效)
  65. * @property {String} bottom 悬浮下边距(仅在fixed为true时有效)
  66. * @property {String} defaultErrorImg 图片加载失败显示的默认图片
  67. * @example <fs-avatar size="100rpx" src="***"></fs-avatar>
  68. */
  69. export default {
  70. name: 'fs-avatar'
  71. }
  72. </script>
  73. <script setup>
  74. import { computed, inject, ref } from 'vue'
  75. import config from '@/utils/config'
  76. const props = defineProps({
  77. src: String,
  78. shape: {
  79. type: String,
  80. default: 'circle', // square, circle
  81. validator(value) {
  82. return ['circle', 'square'].includes(value)
  83. }
  84. },
  85. size: {
  86. type: String,
  87. default: '80rpx'
  88. },
  89. width: String,
  90. height: String,
  91. bgColor: String,
  92. bgColorType: {
  93. type: String,
  94. default: 'primary',
  95. validator(value) {
  96. return ['primary', 'success', 'info', 'warning', 'danger'].includes(value)
  97. }
  98. },
  99. border: Boolean,
  100. borderWidth: {
  101. type: String,
  102. default: '4rpx'
  103. },
  104. borderColor: {
  105. type: String,
  106. default: '#fff'
  107. },
  108. lazyLoad: Boolean,
  109. imageMode: {
  110. type: String,
  111. default: 'scaleToFill'
  112. },
  113. preview: Boolean,
  114. radius: Boolean,
  115. link: String,
  116. linkType: {
  117. type: String,
  118. default: 'navigateTo'
  119. },
  120. fixed: Boolean,
  121. right: {
  122. type: String,
  123. default: '40rpx'
  124. },
  125. bottom: {
  126. type: String,
  127. default: '60rpx'
  128. },
  129. defaultErrorImg: {
  130. type: String,
  131. default: config.defaultErrorImg
  132. }
  133. })
  134. const emits = defineEmits(['click'])
  135. const avatarGroup = inject('avatarGroup', {})
  136. const borderStyle = computed(() => {
  137. return props.border || avatarGroup.border ? `${props.borderWidth} solid ${props.borderColor}` : 'none'
  138. })
  139. const handleClick = () => {
  140. if (props.link) {
  141. uni[props.linkType]({
  142. url: props.link
  143. })
  144. }
  145. emits('click')
  146. }
  147. const handlePreview = () => {
  148. if (props.preview) {
  149. uni.previewImage({
  150. urls: [props.src || errImg.value]
  151. })
  152. }
  153. }
  154. const errImg = ref('')
  155. const handleError = e => {
  156. errImg.value = props.defaultErrorImg
  157. }
  158. </script>
  159. <style lang="scss" scoped>
  160. .fs-avatar {
  161. display: inline-block;
  162. white-space: nowrap;
  163. position: relative;
  164. overflow: hidden;
  165. vertical-align: middle;
  166. text-align: center;
  167. &.radius {
  168. border-radius: var(--radius);
  169. }
  170. &.circle,
  171. &.circle &-img {
  172. border-radius: 50%;
  173. }
  174. &.fixed {
  175. position: fixed;
  176. z-index: 50;
  177. margin-bottom: var(--window-bottom);
  178. }
  179. &-img {
  180. width: 100%;
  181. height: 100%;
  182. object-fit: cover;
  183. }
  184. &-slot {
  185. width: 100%;
  186. height: 100%;
  187. display: flex;
  188. justify-content: center;
  189. align-items: center;
  190. color: #fff;
  191. white-space: normal;
  192. line-height: 1;
  193. }
  194. }
  195. </style>