fs-avatar.vue 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  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. })
  84. const emits = defineEmits(['click'])
  85. const avatarGroup = inject('avatarGroup', {})
  86. const borderStyle = computed(() => {
  87. return (props.border || avatarGroup.border) ? `${ props.borderWidth} solid ${ props.borderColor}` : 'none'
  88. })
  89. const handleClick = () => {
  90. if (props.link) {
  91. uni[props.linkType]({
  92. url: props.link
  93. })
  94. }
  95. emits('click')
  96. }
  97. const handlePreview = () => {
  98. if (props.preview) {
  99. uni.previewImage({
  100. urls: [props.src || errImg.value]
  101. })
  102. }
  103. }
  104. const errImg = ref('')
  105. const handleError = e => {
  106. errImg.value = props.defaultErrorImg
  107. }
  108. </script>
  109. <style lang="scss" scoped>
  110. .fs-avatar {
  111. display: inline-block;
  112. white-space: nowrap;
  113. position: relative;
  114. overflow: hidden;
  115. vertical-align: middle;
  116. text-align: center;
  117. &.radius {
  118. border-radius: var(--radius);
  119. }
  120. &.circle,
  121. &.circle &-img {
  122. border-radius: 50%;
  123. }
  124. &.fixed{
  125. position: fixed;
  126. z-index: 50;
  127. margin-bottom: var(--window-bottom);
  128. }
  129. &-img {
  130. width: 100%;
  131. height: 100%;
  132. object-fit: cover;
  133. }
  134. &-slot {
  135. width: 100%;
  136. height: 100%;
  137. display: flex;
  138. justify-content: center;
  139. align-items: center;
  140. color: #fff;
  141. }
  142. }
  143. </style>