fs-avatar.vue 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  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 class="fs-avatar-img" :src="errImg" v-if="errImg" :lazy-load="lazyLoad" :mode="imageMode" @click="handlePreview" />
  22. <image class="fs-avatar-img" :src="src" v-if="src" :lazy-load="lazyLoad" :mode="imageMode" @click="handlePreview" @error="handleError" />
  23. <view v-else class="fs-avatar-slot" :class="['bg-' + bgColorType]" :style="{backgroundColor:bgColor}">
  24. <slot></slot>
  25. </view>
  26. </view>
  27. </template>
  28. <script setup>
  29. import { computed, inject, ref } from 'vue'
  30. import config from '@/utils/config'
  31. const props = defineProps({
  32. src: String,
  33. shape: {
  34. type: String,
  35. default: 'circle', // square, circle
  36. validator(value) {
  37. return ['circle', 'square'].includes(value)
  38. }
  39. },
  40. size: {
  41. type: String,
  42. default: '80rpx'
  43. },
  44. width: String,
  45. height: String,
  46. bgColor: String,
  47. bgColorType: {
  48. type: String,
  49. default: 'primary',
  50. validator(value) {
  51. return ['primary', 'success', 'info', 'warning', 'danger'].includes(value)
  52. }
  53. },
  54. border: Boolean,
  55. borderWidth: {
  56. type: String,
  57. default: '4rpx'
  58. },
  59. borderColor: {
  60. type: String,
  61. default: '#fff'
  62. },
  63. lazyLoad: Boolean,
  64. imageMode: {
  65. type: String,
  66. default: 'scaleToFill'
  67. },
  68. preview: Boolean,
  69. radius: Boolean,
  70. link: String,
  71. linkType: {
  72. type: String,
  73. default: 'navigateTo'
  74. },
  75. fixed: Boolean,
  76. right: {
  77. type: String,
  78. default: '40rpx'
  79. },
  80. bottom: {
  81. type: String,
  82. default: '60rpx'
  83. },
  84. defaultErrorImg: {
  85. type: String,
  86. default: config.defaultErrorImg
  87. }
  88. })
  89. const emits = defineEmits(['click'])
  90. const avatarGroup = inject('avatarGroup', {})
  91. const borderStyle = computed(() => {
  92. return (props.border || avatarGroup.border) ? `${ props.borderWidth} solid ${ props.borderColor}` : 'none'
  93. })
  94. const handleClick = () => {
  95. if (props.link) {
  96. uni[props.linkType]({
  97. url: props.link
  98. })
  99. }
  100. emits('click')
  101. }
  102. const handlePreview = () => {
  103. if (props.preview) {
  104. uni.previewImage({
  105. urls: [props.src || errImg.value]
  106. })
  107. }
  108. }
  109. const errImg = ref('')
  110. const handleError = e => {
  111. errImg.value = props.defaultErrorImg
  112. }
  113. </script>
  114. <style lang="scss" scoped>
  115. .fs-avatar {
  116. display: inline-block;
  117. white-space: nowrap;
  118. position: relative;
  119. overflow: hidden;
  120. vertical-align: middle;
  121. text-align: center;
  122. &.radius {
  123. border-radius: var(--radius);
  124. }
  125. &.circle,
  126. &.circle &-img {
  127. border-radius: 50%;
  128. }
  129. &.fixed{
  130. position: fixed;
  131. z-index: 50;
  132. margin-bottom: var(--window-bottom);
  133. }
  134. &-img {
  135. width: 100%;
  136. height: 100%;
  137. object-fit: cover;
  138. }
  139. &-slot {
  140. width: 100%;
  141. height: 100%;
  142. display: flex;
  143. justify-content: center;
  144. align-items: center;
  145. color: #fff;
  146. white-space: normal;
  147. line-height: 1;
  148. }
  149. }
  150. </style>