index.tsx 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  1. import { closeToast, Icon, Image, showLoadingToast, showToast, Uploader } from 'vant'
  2. import { defineComponent, PropType, ref } from 'vue'
  3. import styles from './index.module.less'
  4. import { useCustomFieldValue } from '@vant/use'
  5. import { postMessage } from '@/helpers/native-message'
  6. import umiRequest from 'umi-request'
  7. import iconUploader from '@common/images/icon-upload.png'
  8. import iconUploadClose from '@common/images/icon-upload-close.png'
  9. import request from '@/helpers/request'
  10. import { getOssUploadUrl, state } from '@/state'
  11. export default defineComponent({
  12. name: 'col-upload',
  13. props: {
  14. modelValue: {
  15. type: Array,
  16. default: () => []
  17. },
  18. deletable: {
  19. type: Boolean,
  20. default: true
  21. },
  22. maxCount: {
  23. type: Number,
  24. default: 1
  25. },
  26. native: {
  27. // 是否原生上传
  28. type: Boolean,
  29. default: false
  30. },
  31. uploadSize: {
  32. // 上传图片大小
  33. type: Number,
  34. default: 5
  35. },
  36. uploadType: {
  37. type: String as PropType<'IMAGE' | 'VIDEO'>,
  38. default: 'IMAGE'
  39. },
  40. accept: {
  41. type: String,
  42. default: 'image/*'
  43. },
  44. onUploadChange: {
  45. type: Function,
  46. default: (url: string) => {}
  47. },
  48. bucket: {
  49. type: String,
  50. default: 'gyt'
  51. },
  52. path: {
  53. type: String,
  54. default: ''
  55. },
  56. uploadIcon: {
  57. type: String,
  58. default: iconUploader
  59. },
  60. size: {
  61. type: String,
  62. default: 'default'
  63. },
  64. disabled: {
  65. type: Boolean,
  66. default: false
  67. }
  68. },
  69. methods: {
  70. nativeUpload() {
  71. if (this.disabled) {
  72. return
  73. }
  74. const type = this.uploadType === 'VIDEO' ? 'video' : 'img'
  75. postMessage(
  76. {
  77. api: 'chooseFile',
  78. content: { type: type, max: 1, bucket: this.bucket, path: this.path }
  79. },
  80. (res: any) => {
  81. console.log(res, 'fileUrl')
  82. // 判断是否是多选
  83. if (this.maxCount > 1) {
  84. this.$emit('update:modelValue', [...this.modelValue, res.fileUrl])
  85. this.onUploadChange([...this.modelValue, res.fileUrl])
  86. } else {
  87. this.$emit('update:modelValue', [res.fileUrl])
  88. this.onUploadChange([res.fileUrl])
  89. }
  90. }
  91. )
  92. },
  93. beforeRead(file: any) {
  94. console.log(file, 'beforeRead')
  95. const isLt2M = file.size / 1024 / 1024 < this.uploadSize
  96. if (!isLt2M) {
  97. showToast(`上传文件大小不能超过 ${this.uploadSize}MB`)
  98. return false
  99. }
  100. return true
  101. },
  102. beforeDelete(file: any, detail: { index: any }) {
  103. // this.dataModel.splice(detail.index, 1)
  104. return true
  105. },
  106. async afterRead(file: any, detail: any) {
  107. try {
  108. file.status = 'uploading'
  109. file.message = '上传中...'
  110. await this.uploadFile(file.file)
  111. } catch (error) {
  112. closeToast()
  113. }
  114. },
  115. onClose(e: any, item: any) {
  116. const models = this.modelValue
  117. const index = models.findIndex((model) => model == item)
  118. if (index > -1) {
  119. models.splice(index, 1)
  120. this.$emit('update:modelValue', models)
  121. this.onUploadChange()
  122. }
  123. e.stopPropagation()
  124. },
  125. async getFile(file: any) {
  126. try {
  127. await this.uploadFile(file)
  128. } catch {
  129. //
  130. }
  131. },
  132. async uploadFile(file: any) {
  133. // 上传文件
  134. try {
  135. // 获取签名
  136. if (state.platformType === 'SCHOOL') {
  137. state.platformApi = '/api-school'
  138. } else if (state.platformType === 'TEACHER') {
  139. state.platformApi = '/api-teacher'
  140. } else if (state.platformType === 'STUDENT') {
  141. state.platformApi = '/api-student'
  142. }
  143. const signUrl = state.platformApi + '/open/getUploadSign'
  144. const tempName = file.name || ''
  145. const fileName = this.path + (tempName && tempName.replace(/ /gi, '_'))
  146. const key = new Date().getTime() + fileName
  147. console.log(file)
  148. const res = await request.post(signUrl, {
  149. data: {
  150. filename: fileName,
  151. bucketName: this.bucket,
  152. postData: {
  153. filename: fileName,
  154. acl: 'public-read',
  155. key: key,
  156. unknowValueField: []
  157. }
  158. }
  159. })
  160. showLoadingToast({
  161. message: '加载中...',
  162. forbidClick: true,
  163. loadingType: 'spinner',
  164. duration: 0
  165. })
  166. const obj = {
  167. policy: res.data.policy,
  168. signature: res.data.signature,
  169. key: key,
  170. KSSAccessKeyId: res.data.kssAccessKeyId,
  171. acl: 'public-read',
  172. name: fileName
  173. }
  174. const formData = new FormData()
  175. for (const key in obj) {
  176. formData.append(key, obj[key])
  177. }
  178. formData.append('file', file, fileName)
  179. await umiRequest(getOssUploadUrl(this.bucket), {
  180. method: 'POST',
  181. data: formData
  182. })
  183. console.log(getOssUploadUrl(this.bucket) + key)
  184. const uploadUrl = getOssUploadUrl(this.bucket) + key
  185. closeToast()
  186. // 判断是否是多选
  187. if (this.maxCount > 1) {
  188. this.$emit('update:modelValue', [...this.modelValue, uploadUrl])
  189. this.onUploadChange([...this.modelValue, uploadUrl])
  190. } else {
  191. this.$emit('update:modelValue', [uploadUrl])
  192. this.onUploadChange([uploadUrl])
  193. }
  194. } catch (error) {
  195. console.log(error, 'uploadFile')
  196. }
  197. }
  198. },
  199. render() {
  200. useCustomFieldValue(() => this.modelValue)
  201. return (
  202. <div class={styles['uploader-section']}>
  203. {this.modelValue.length > 0 &&
  204. this.maxCount > 1 &&
  205. this.modelValue.map((item: any) => (
  206. <div class={[styles.uploader, styles[this.size]]}>
  207. {/* 删除按钮 */}
  208. {this.deletable && !this.disabled && (
  209. <Icon
  210. name="cross"
  211. onClick={(e: any) => this.onClose(e, item)}
  212. class={styles['img-close']}
  213. />
  214. )}
  215. <div class={['van-uploader__upload']}>
  216. {this.uploadType === 'IMAGE' ? (
  217. <Image src={item} class={styles.previewImg} fit="cover" />
  218. ) : (
  219. <video
  220. ref="videoUpload"
  221. style={{ backgroundColor: '#F8F8F8' }}
  222. class={styles.previewImg}
  223. src={item + '#t=1,4'}
  224. />
  225. )}
  226. </div>
  227. </div>
  228. ))}
  229. {this.native ? (
  230. this.maxCount > 1 ? (
  231. <div class={[styles.uploader, styles[this.size]]} onClick={this.nativeUpload}>
  232. <Icon name={this.uploadIcon} class={['van-uploader__upload']} size="32" />
  233. </div>
  234. ) : (
  235. <div class={[styles.uploader, styles[this.size]]} onClick={this.nativeUpload}>
  236. {this.modelValue.length > 0 ? (
  237. <div class={['van-uploader__upload']}>
  238. {this.modelValue.map((item: any) => (
  239. <>
  240. {/* 删除按钮 */}
  241. {this.deletable && !this.disabled && (
  242. <Icon
  243. name="cross"
  244. onClick={(e: any) => this.onClose(e, item)}
  245. class={[styles['img-close'], styles.singleImgClose]}
  246. />
  247. )}
  248. {this.uploadType === 'IMAGE' ? (
  249. <Image fit="cover" position="center" class={styles.uploadImg} src={item} />
  250. ) : (
  251. <video
  252. ref="videoUpload"
  253. class={styles.uploadImg}
  254. style={{ backgroundColor: '#F8F8F8' }}
  255. src={item + '#t=1,4'}
  256. />
  257. )}
  258. </>
  259. ))}
  260. </div>
  261. ) : (
  262. <Icon name={this.uploadIcon} class={['van-uploader__upload']} size="32" />
  263. )}
  264. </div>
  265. )
  266. ) : this.maxCount > 1 ? (
  267. <Uploader
  268. class={[styles.uploader, styles[this.size]]}
  269. afterRead={this.afterRead}
  270. beforeRead={this.beforeRead}
  271. beforeDelete={this.beforeDelete}
  272. uploadIcon={this.uploadIcon}
  273. disabled={this.modelValue.length === this.maxCount || this.disabled}
  274. accept={this.accept}
  275. />
  276. ) : (
  277. <Uploader
  278. class={[styles.uploader, styles[this.size]]}
  279. afterRead={this.afterRead}
  280. beforeRead={this.beforeRead}
  281. beforeDelete={this.beforeDelete}
  282. uploadIcon={this.uploadIcon}
  283. accept={this.accept}
  284. disabled={this.disabled}
  285. >
  286. {this.modelValue.length > 0 ? (
  287. <div class={['van-uploader__upload']}>
  288. {this.modelValue.map((item: any) => (
  289. <>
  290. {/* 删除按钮 */}
  291. {this.deletable && !this.disabled && (
  292. <Icon
  293. name="cross"
  294. onClick={(e: any) => this.onClose(e, item)}
  295. class={[styles['img-close'], styles.singleImgClose]}
  296. />
  297. )}
  298. {this.uploadType === 'IMAGE' ? (
  299. <Image fit="cover" position="center" class={styles.uploadImg} src={item} />
  300. ) : (
  301. <video
  302. ref="videoUpload"
  303. class={styles.uploadImg}
  304. style={{ backgroundColor: '#F8F8F8' }}
  305. src={item + '#t=1,4'}
  306. />
  307. )}
  308. </>
  309. ))}
  310. </div>
  311. ) : (
  312. <Icon name={this.uploadIcon} class={['van-uploader__upload']} size="32" />
  313. )}
  314. </Uploader>
  315. )}
  316. </div>
  317. )
  318. }
  319. })