custom-image.vue 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. <template>
  2. <view :style="[viewStyle]" :class="{'custom-image': true, 'image-round': round}" @click="onClick">
  3. <image v-if="!error" :src="src" :mode="mode" :lazy-load="lazyLoad" class="image" :show-menu-by-longpress="showMenuByLongpress"
  4. @load="onLoaded" @error="onErrored"></image>
  5. <view v-if="loading && showLoading" class="loading-wrap image">
  6. <slot v-if="useLoadingSlot" name="loading"></slot>
  7. <u-icon color="#aaa" v-else name="photo-fill" size="45"></u-icon>
  8. </view>
  9. <view v-if="error && showError" class="error-wrap image">
  10. <slot v-if="useErrorSlot" name="error"></slot>
  11. <u-icon color="#aaa" v-else name="error-circle-fill" size="45"></u-icon>
  12. <text class="sm">加载失败</text>
  13. </view>
  14. </view>
  15. </template>
  16. <script>
  17. export default {
  18. props: {
  19. src: {
  20. type: String,
  21. },
  22. round: Boolean,
  23. width: {
  24. type: null
  25. },
  26. height: {
  27. type: null
  28. },
  29. radius: null,
  30. lazyLoad: {
  31. type: Boolean,
  32. default: true
  33. },
  34. useErrorSlot: Boolean,
  35. useLoadingSlot: Boolean,
  36. showMenuByLongpress: Boolean,
  37. mode: {
  38. type: String,
  39. default: 'scaleToFill'
  40. },
  41. showError: {
  42. type: Boolean,
  43. default: true
  44. },
  45. showLoading: {
  46. type: Boolean,
  47. default: true
  48. },
  49. customStyle: {
  50. type: Object,
  51. default: () => {}
  52. }
  53. },
  54. data() {
  55. return {
  56. error: false,
  57. loading: true,
  58. viewStyle: {}
  59. }
  60. },
  61. created() {
  62. this.setStyle();
  63. },
  64. methods: {
  65. setStyle() {
  66. const {
  67. width,
  68. height,
  69. radius
  70. } = this
  71. let style = {};
  72. if (width) {
  73. style.width = width
  74. }
  75. if (height) {
  76. style.height = height
  77. }
  78. if (radius) {
  79. style['overflow'] = 'hidden'
  80. style['border-radius'] = radius
  81. }
  82. this.viewStyle = style;
  83. if(this.customStyle) {
  84. this.viewStyle = Object.assign(this.viewStyle, this.customStyle)
  85. }
  86. },
  87. onLoaded(event) {
  88. this.loading = false
  89. this.$emit('load', event.detail);
  90. },
  91. onErrored(event) {
  92. this.error = false
  93. this.loading = true
  94. this.$emit('error', event.detail);
  95. },
  96. onClick(event) {
  97. this.$emit('click', event.detail);
  98. }
  99. },
  100. watch: {
  101. src() {
  102. this.error = false
  103. this.loading = true
  104. },
  105. width() {
  106. this.setStyle()
  107. },
  108. height() {
  109. this.setStyle()
  110. }
  111. }
  112. }
  113. </script>
  114. <style lang="scss">
  115. .custom-image {
  116. position: relative;
  117. display: block;
  118. width: 100%;
  119. height: 100%;
  120. &.image-round {
  121. overflow: hidden;
  122. border-radius: 50%;
  123. }
  124. .image {
  125. display: block;
  126. width: 100%;
  127. height: 100%;
  128. }
  129. .loading-wrap,
  130. .error-wrap {
  131. position: absolute;
  132. top: 0;
  133. left: 0;
  134. display: flex;
  135. flex-direction: column;
  136. align-items: center;
  137. justify-content: center;
  138. color: #969799;
  139. font-size: 28rpx;
  140. background-color: #f7f8fa;
  141. }
  142. }
  143. </style>