fs-avatar.vue 2.8 KB

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