message-image.vue 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. <template>
  2. <div class="message-image" @click.self="toggleShow" ref="skeleton">
  3. <img
  4. class="message-img"
  5. :src="data.url"
  6. :width="data.width"
  7. :height="data.height"
  8. :style="
  9. isH5
  10. ? {
  11. maxWidth: data.width ? data.width + 'Px' : 'calc(100vw - 180Px)',
  12. maxHeight: data.height
  13. ? data.height + 'Px'
  14. : 'calc(100vw - 180Px)'
  15. }
  16. : {}
  17. "
  18. />
  19. <div class="progress" v-if="data.progress">
  20. <progress :value="data.progress" max="1"></progress>
  21. </div>
  22. <div class="dialog" v-if="show" @click.self="toggleShow">
  23. <header v-if="!isH5">
  24. <i class="icon icon-close" @click.stop="toggleShow"></i>
  25. </header>
  26. <div
  27. class="dialog-box"
  28. :class="[isH5 ? 'dialog-box-h5' : '']"
  29. @click.self="toggleShow"
  30. >
  31. <img
  32. :class="[isWidth ? 'isWidth' : 'isHeight']"
  33. :src="data.message.payload.imageInfoArray[0].url"
  34. @click.self="toggleShow"
  35. />
  36. <div v-if="isH5" class="dialog-box-h5-footer">
  37. <p @click="toggleShow">
  38. <img src="../../../assets/icon/close-image.png" />
  39. </p>
  40. <p @click.stop="downloadImage(data.message)">
  41. <img src="../../../assets/icon/downaload-image.png" />
  42. </p>
  43. </div>
  44. </div>
  45. </div>
  46. </div>
  47. </template>
  48. <script lang="ts">
  49. import {
  50. defineComponent,
  51. watchEffect,
  52. reactive,
  53. toRefs,
  54. computed,
  55. ref,
  56. nextTick
  57. } from 'vue';
  58. import { handleSkeletonSize } from '../utils/utils';
  59. export default defineComponent({
  60. props: {
  61. data: {
  62. type: Object,
  63. default: () => ({})
  64. },
  65. isH5: {
  66. type: Boolean,
  67. default: false
  68. }
  69. },
  70. emits: ['uploading', 'previewImage'],
  71. setup(props: any, ctx: any) {
  72. const data = reactive({
  73. data: {
  74. progress: 0
  75. },
  76. show: false
  77. });
  78. const skeleton: any = ref();
  79. const isWidth = computed(() => {
  80. // eslint-disable-next-line no-unsafe-optional-chaining
  81. const { width = 0, height = 0 } = (data.data as any)?.message?.payload
  82. ?.imageInfoArray[0];
  83. return width >= height;
  84. });
  85. watchEffect(() => {
  86. data.data = props.data;
  87. if (!data.data) return;
  88. nextTick(() => {
  89. if (!data.data.progress) {
  90. const { width = 0, height = 0 } = data.data as any;
  91. if (width === 0 || height === 0) return;
  92. const containerWidth =
  93. document.getElementById('app')?.clientWidth || 0;
  94. const max = props.isH5 ? Math.min(containerWidth - 180, 300) : 300;
  95. const size = handleSkeletonSize(width, height, max, max);
  96. skeleton?.value?.style &&
  97. (skeleton.value.style.width = `${size.width}Px`);
  98. skeleton?.value?.style &&
  99. (skeleton.value.style.height = `${size.height}Px`);
  100. } else {
  101. ctx.emit('uploading');
  102. }
  103. });
  104. });
  105. const toggleShow = () => {
  106. if (!data.data.progress) {
  107. ctx.emit('previewImage', (data.data as any).message);
  108. }
  109. };
  110. const downloadImage = (message: any) => {
  111. const targetImage = document.createElement('a');
  112. const downloadImageName = message.payload.imageInfoArray[0].instanceID;
  113. targetImage.setAttribute('download', downloadImageName);
  114. const image = new Image();
  115. image.src = message.payload.imageInfoArray[0].url;
  116. image.setAttribute('crossOrigin', 'Anonymous');
  117. image.onload = () => {
  118. targetImage.href = getImageDataURL(image);
  119. targetImage.click();
  120. };
  121. };
  122. const getImageDataURL = (image: any) => {
  123. const canvas = document.createElement('canvas');
  124. canvas.width = image.width;
  125. canvas.height = image.height;
  126. const ctx = canvas.getContext('2d');
  127. ctx?.drawImage(image, 0, 0, image.width, image.height);
  128. const extension = image.src
  129. .substring(image.src.lastIndexOf('.') + 1)
  130. .toLowerCase();
  131. return canvas.toDataURL(`image/${extension}`, 1);
  132. };
  133. return {
  134. ...toRefs(data),
  135. toggleShow,
  136. skeleton,
  137. isWidth,
  138. downloadImage
  139. };
  140. }
  141. });
  142. </script>
  143. <style lang="scss" scoped>
  144. @import url('../../../styles/common.scss');
  145. @import url('../../../styles/icon.scss');
  146. .message-image {
  147. position: relative;
  148. overflow: hidden;
  149. opacity: 1;
  150. .message-img {
  151. max-width: min(calc(100vw - 180Px), 300Px);
  152. max-height: min(calc(100vw - 180Px), 300Px);
  153. width: inherit;
  154. height: inherit;
  155. }
  156. .progress {
  157. position: absolute;
  158. box-sizing: border-box;
  159. width: 100%;
  160. height: 100%;
  161. padding: 0 20Px;
  162. left: 0;
  163. top: 0;
  164. background: rgba(#000000, 0.5);
  165. display: flex;
  166. align-items: center;
  167. progress {
  168. color: #006eff;
  169. appearance: none;
  170. border-radius: 0.25rem;
  171. background: rgba(#ffffff, 1);
  172. width: 100%;
  173. height: 0.5rem;
  174. &::-webkit-progress-value {
  175. background-color: #006eff;
  176. border-radius: 0.25rem;
  177. }
  178. &::-webkit-progress-bar {
  179. border-radius: 0.25rem;
  180. background: rgba(#ffffff, 1);
  181. }
  182. &::-moz-progress-bar {
  183. color: #006eff;
  184. background: #006eff;
  185. border-radius: 0.25rem;
  186. }
  187. }
  188. }
  189. }
  190. .dialog {
  191. position: fixed;
  192. z-index: 12;
  193. width: 100vw;
  194. height: 100vh;
  195. background: rgba(#000000, 0.3);
  196. top: 0;
  197. left: 0;
  198. display: flex;
  199. flex-direction: column;
  200. align-items: center;
  201. header {
  202. display: flex;
  203. justify-content: flex-end;
  204. background: rgba(0, 0, 0, 0.49);
  205. width: 100%;
  206. box-sizing: border-box;
  207. padding: 10Px 10Px;
  208. }
  209. &-box {
  210. display: flex;
  211. flex: 1;
  212. max-height: 100%;
  213. padding: 6rem;
  214. box-sizing: border-box;
  215. justify-content: center;
  216. align-items: center;
  217. }
  218. }
  219. .dialog-box-h5 {
  220. width: 100%;
  221. height: 100%;
  222. background: #000000;
  223. padding: 30Px 0;
  224. display: flex;
  225. flex-direction: column;
  226. &-footer {
  227. position: fixed;
  228. bottom: 10Px;
  229. display: flex;
  230. width: 90vw;
  231. justify-content: space-between;
  232. p {
  233. width: 3.88rem;
  234. height: 3.88rem;
  235. }
  236. img {
  237. width: 100%;
  238. }
  239. i {
  240. padding: 20Px;
  241. }
  242. }
  243. }
  244. .isWidth {
  245. width: 100%;
  246. }
  247. .isHeight {
  248. height: 100%;
  249. }
  250. </style>