index.vue 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. <template>
  2. <el-dialog
  3. :visible.sync="dialogVisible"
  4. custom-class="c-dialog--transparent lg"
  5. :close-on-click-modal="false"
  6. append-to-body
  7. @open="onOpen"
  8. @close="onClose"
  9. @closed="onClosed"
  10. >
  11. <template v-if="contentRender">
  12. <auto-image
  13. v-if="isImage"
  14. class="o-preview u-font-size--3xl"
  15. :src="assetUrl"
  16. retry
  17. />
  18. <video
  19. v-if="isVideo"
  20. class="o-preview"
  21. :src="assetUrl"
  22. autoplay
  23. controls
  24. controlslist="nodownload noremoteplayback"
  25. />
  26. <audio
  27. v-if="isAudio"
  28. :src="assetUrl"
  29. autoplay
  30. controls
  31. controlslist="nodownload noremoteplayback"
  32. />
  33. <video
  34. v-if="isStreamingMedia"
  35. ref="streamingMediaPlayer"
  36. class="o-preview"
  37. autoplay
  38. controls
  39. />
  40. <i
  41. class="o-close el-icon-close has-active u-bold"
  42. @click="hide"
  43. />
  44. <template v-if="isMultipleFiles">
  45. <i
  46. class="o-arrow o-icon el-icon-arrow-left has-active u-bold"
  47. @click="onPresent"
  48. />
  49. <i
  50. class="o-arrow o-icon el-icon-arrow-right has-active u-bold"
  51. @click="onNext"
  52. />
  53. <div class="o-total">
  54. {{ active + 1 }}/{{ total }}
  55. </div>
  56. </template>
  57. </template>
  58. </el-dialog>
  59. </template>
  60. <script>
  61. import HlsJs from 'hls.js'
  62. import { AssetType } from '@/constant'
  63. import { getAssetUrl } from '@/api/asset'
  64. import dialogMixin from '@/mixins/dialog'
  65. const isSupported = HlsJs.isSupported()
  66. const isHttps = location.protocol === 'https:'
  67. export default {
  68. name: 'PreviewDialog',
  69. mixins: [dialogMixin],
  70. data () {
  71. return {
  72. isMultipleFiles: false,
  73. total: 0,
  74. active: 0,
  75. source: null
  76. }
  77. },
  78. computed: {
  79. fileType () {
  80. return this.source?.type
  81. },
  82. isImage () {
  83. return this.fileType === AssetType.IMAGE
  84. },
  85. isVideo () {
  86. return this.fileType === AssetType.VIDEO
  87. },
  88. isAudio () {
  89. return this.fileType === AssetType.AUDIO
  90. },
  91. isStreamingMedia () {
  92. return this.fileType === AssetType.STREAMING_MEDIA
  93. },
  94. assetUrl () {
  95. console.log('assetUrl---:', this.source && getAssetUrl(this.source.url))
  96. return this.source && getAssetUrl(this.source.url)
  97. }
  98. },
  99. beforeDestroy () {
  100. this.onClose()
  101. },
  102. methods: {
  103. show (source) {
  104. if (!source) {
  105. return
  106. }
  107. this.$sources = source.files?.length
  108. ? source.files.map(({ type, keyName }) => {
  109. return { type, url: keyName }
  110. })
  111. : [source]
  112. this.isMultipleFiles = this.$sources.length > 1
  113. this.total = this.$sources.length
  114. this.active = 0
  115. this.source = this.$sources[this.active]
  116. this.dialogVisible = true
  117. console.log('source::', this.$sources)
  118. },
  119. onOpen () {
  120. this.contentRender = true
  121. if (this.isStreamingMedia) {
  122. this.$nextTick(() => {
  123. this.playStreamingMedia()
  124. })
  125. }
  126. },
  127. onClose () {
  128. if (this.hlsjs) {
  129. this.hlsjs.destroy()
  130. this.hlsjs = null
  131. }
  132. },
  133. onPresent () {
  134. this.active = (this.active - 1 + this.total) % this.total
  135. this.source = this.$sources[this.active]
  136. },
  137. onNext () {
  138. this.active = (this.active + 1) % this.total
  139. this.source = this.$sources[this.active]
  140. },
  141. playStreamingMedia () {
  142. const url = isHttps ? this.source.url.replace(/^http:/, '') : this.source.url
  143. if (this.$refs.streamingMediaPlayer.canPlayType('application/vnd.apple.mpegurl')) {
  144. this.$refs.streamingMediaPlayer.src = url
  145. } if (isSupported) {
  146. if (!this.hlsjs) {
  147. this.hlsjs = new HlsJs()
  148. this.hlsjs.attachMedia(this.$refs.streamingMediaPlayer)
  149. this.hlsjs.on(HlsJs.Events.MANIFEST_PARSED, () => {
  150. this.$refs.streamingMediaPlayer.play()
  151. })
  152. this.hlsjs.on(HlsJs.Events.ERROR, e => {
  153. console.log('error', e)
  154. })
  155. }
  156. this.hlsjs.loadSource(url)
  157. }
  158. }
  159. }
  160. }
  161. </script>
  162. <style lang="scss" scoped>
  163. .o-preview {
  164. max-width: 100%;
  165. max-height: 100%;
  166. color: #fff;
  167. line-height: 0;
  168. }
  169. .o-close {
  170. position: absolute;
  171. top: 0;
  172. right: 0;
  173. padding: $padding--md $padding--lg;
  174. color: #fff;
  175. font-size: $font-size--xl;
  176. transform: translateY(-100%);
  177. }
  178. .o-arrow {
  179. position: absolute;
  180. color: #fff;
  181. top: 50%;
  182. &.el-icon-arrow-left {
  183. left: 0;
  184. transform: translate(-100%, -50%);
  185. }
  186. &.el-icon-arrow-right {
  187. right: 0;
  188. transform: translate(100%, -50%);
  189. }
  190. }
  191. .o-total {
  192. position: absolute;
  193. left: 50%;
  194. bottom: -$font-size--xl * 2;
  195. color: #fff;
  196. font-size: $font-size--xl;
  197. transform: translateX(-50%);
  198. }
  199. </style>