index.tsx 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. import request from '@/helpers/request'
  2. import { Icon, Toast, Uploader, Image } from 'vant'
  3. import { defineComponent } from 'vue'
  4. import styles from './index.module.less'
  5. import { useCustomFieldValue } from '@vant/use'
  6. import { browser } from '@/helpers/utils'
  7. import iconUploader from '@common/images/icon_uploader_video.png'
  8. import { postMessage } from '@/helpers/native-message'
  9. export default defineComponent({
  10. name: 'ColUploadVideo',
  11. props: {
  12. modelValue: String,
  13. posterUrl: String,
  14. tips: {
  15. type: String,
  16. default: '点击上传'
  17. },
  18. nativeUpload: {
  19. // 是否使用原生上传, 且当前环境为app才会生效
  20. type: Boolean,
  21. default: true
  22. },
  23. size: {
  24. type: Number,
  25. default: 30
  26. },
  27. deletable: {
  28. type: Boolean,
  29. default: true
  30. }
  31. },
  32. data() {
  33. return {
  34. posterUrlInner: ''
  35. }
  36. },
  37. async mounted() {
  38. if (this.modelValue) {
  39. const urlImg = await this.getVideoBase64(this.modelValue)
  40. this.posterUrlInner = urlImg as string
  41. this.$emit('update:posterUrl', urlImg as string)
  42. }
  43. this.posterUrlInner = this.posterUrlInner || this.posterUrl || ''
  44. },
  45. methods: {
  46. beforeRead(file: any) {
  47. const isLt2M = file.size / 1024 / 1024 < this.size
  48. console.log(this.size)
  49. if (!isLt2M) {
  50. Toast(`上传视频大小不能超过 ${this.size}MB`)
  51. return false
  52. }
  53. return true
  54. },
  55. beforeDelete(file: any, detail: { index: any }) {
  56. // this.dataModel.splice(detail.index, 1)
  57. return true
  58. },
  59. async afterRead(file: any, detail: any) {
  60. try {
  61. file.status = 'uploading'
  62. file.message = '上传中...'
  63. let formData = new FormData()
  64. formData.append('file', file.file)
  65. let res = await request.post('/api-teacher/uploadFile', {
  66. data: formData
  67. })
  68. const url = res.data.url
  69. const urlImg = await this.getVideoBase64(url)
  70. this.posterUrlInner = urlImg as string
  71. this.$emit('update:modelValue', url)
  72. this.$emit('update:posterUrl', urlImg as string)
  73. } catch (error) {
  74. //
  75. }
  76. },
  77. onClose(e: any) {
  78. this.posterUrlInner = ''
  79. this.$emit('update:modelValue', null)
  80. e.stopPropagation()
  81. },
  82. onNativeUpload() {
  83. postMessage(
  84. { api: 'chooseFile', content: { type: 'video' } },
  85. (res: any) => {
  86. this.posterUrlInner = res.firstFrameImg
  87. this.$emit('update:modelValue', res.fileUrl)
  88. this.$emit('update:posterUrl', res.firstFrameImg)
  89. }
  90. )
  91. },
  92. getVideoBase64(url: string) {
  93. return new Promise(function (resolve) {
  94. let dataURL = ''
  95. const video = document.createElement('video')
  96. video.setAttribute('crossOrigin', 'anonymous') // 处理跨域
  97. video.setAttribute('src', url)
  98. video.setAttribute('preload', 'auto')
  99. video.addEventListener('loadeddata', function () {
  100. const canvas = document.createElement('canvas')
  101. console.log('video.clientWidth', video.videoWidth) // 视频宽
  102. console.log('video.clientHeight', video.videoHeight) // 视频高
  103. const width = video.videoWidth || 750 // canvas的尺寸和图片一样
  104. const height = video.videoHeight || 500 // 设置默认宽高为 750 * 500
  105. canvas.width = width
  106. canvas.height = height
  107. ;(canvas as any)
  108. .getContext('2d')
  109. .drawImage(video, 0, 0, width, height) // 绘制canvas
  110. dataURL = canvas.toDataURL('image/jpeg') // 转换为base64
  111. resolve(dataURL)
  112. })
  113. })
  114. }
  115. },
  116. render() {
  117. useCustomFieldValue(() => this.modelValue)
  118. return (
  119. <div class={styles['uploader-section']}>
  120. {this.modelValue && this.deletable ? (
  121. <Icon
  122. name="cross"
  123. onClick={this.onClose}
  124. class={styles['img-close']}
  125. />
  126. ) : null}
  127. {browser().isApp && this.nativeUpload ? (
  128. <div onClick={this.onNativeUpload} style={{ height: '100%' }}>
  129. {this.modelValue ? (
  130. <video
  131. ref="videoUpload"
  132. class={styles.uploadImg}
  133. src={this.modelValue}
  134. poster={this.posterUrlInner}
  135. />
  136. ) : (
  137. <div class={styles.uploader}>
  138. <Icon name={iconUploader} size="32" />
  139. <p class={styles.uploaderText}>{this.tips}</p>
  140. </div>
  141. )}
  142. </div>
  143. ) : (
  144. <>
  145. {/* @ts-ignore */}
  146. <Uploader
  147. accept=".mp4"
  148. afterRead={this.afterRead}
  149. beforeRead={this.beforeRead}
  150. beforeDelete={this.beforeDelete}
  151. v-slots={{
  152. default: () =>
  153. this.modelValue ? (
  154. <video
  155. ref="videoUpload"
  156. class={styles.uploadImg}
  157. src={this.modelValue}
  158. poster={this.posterUrlInner}
  159. />
  160. ) : (
  161. <div class={styles.uploader}>
  162. <Icon name={iconUploader} size="32" />
  163. <p class={styles.uploaderText}>{this.tips}</p>
  164. </div>
  165. )
  166. }}
  167. />
  168. </>
  169. )}
  170. </div>
  171. )
  172. }
  173. })