fs-avatar.vue 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  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': marginLeft
  18. }"
  19. @click="handleClick"
  20. >
  21. <image class="fs-avatar-img" :src="src" v-if="src" :lazy-load="lazyLoad" :mode="imageMode" />
  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. radius: Boolean,
  67. link: String,
  68. linkType: {
  69. type: String,
  70. default: 'navigateTo'
  71. },
  72. fixed: Boolean,
  73. right: {
  74. type: String,
  75. default: '30rpx'
  76. },
  77. bottom: {
  78. type: String,
  79. default: '30rpx'
  80. }
  81. })
  82. const emits = defineEmits(['click'])
  83. const marginLeft = inject('marginLeft', 0)
  84. const borderStyle = computed(() => props.border ? `${ props.borderWidth} solid ${ props.borderColor}` : 'none')
  85. const handleClick = () => {
  86. if (props.link) {
  87. uni[props.linkType]({
  88. url: props.link
  89. })
  90. }
  91. emits('click')
  92. }
  93. </script>
  94. <style lang="scss" scoped>
  95. .fs-avatar {
  96. display: inline-block;
  97. white-space: nowrap;
  98. position: relative;
  99. overflow: hidden;
  100. vertical-align: middle;
  101. text-align: center;
  102. &.radius {
  103. border-radius: var(--radius);
  104. }
  105. &.circle,
  106. &.circle &-img {
  107. border-radius: 50%;
  108. }
  109. &.fixed{
  110. position: fixed;
  111. z-index: 50;
  112. margin-bottom: var(--window-bottom);
  113. }
  114. &-img {
  115. width: 100%;
  116. height: 100%;
  117. object-fit: cover;
  118. }
  119. &-slot {
  120. width: 100%;
  121. height: 100%;
  122. display: flex;
  123. justify-content: center;
  124. align-items: center;
  125. color: #fff;
  126. }
  127. & + &{
  128. margin-left: -20rpx;
  129. }
  130. }
  131. </style>