index.tsx 11 KB

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