fs-avatar.vue 2.5 KB

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