Non puoi selezionare più di 25 argomenti Gli argomenti devono iniziare con una lettera o un numero, possono includere trattini ('-') e possono essere lunghi fino a 35 caratteri.
 
 
 

963 righe
30 KiB

  1. <template>
  2. <view class="u-upload" :style="[addStyle(customStyle)]">
  3. <view class="u-upload__wrap" >
  4. <template v-if="previewImage">
  5. <view
  6. class="u-upload__wrap__preview"
  7. v-for="(item, index) in lists"
  8. :key="index"
  9. >
  10. <image
  11. v-if="(item.isImage || (item.type && item.type === 'image')) && (item.thumb || item.url)"
  12. :src="item.thumb || item.url"
  13. :mode="imageMode"
  14. class="u-upload__wrap__preview__image"
  15. @tap="onClickPreview(item, index)"
  16. :style="[{
  17. width: addUnit(width),
  18. height: addUnit(height),
  19. backgroundColor: upThemeVar('--up-card-bg-color', 'rgb(242, 242, 242)'),
  20. borderColor: upThemeVar('--up-border-color', '#e5e7eb'),
  21. borderWidth: '1px',
  22. borderStyle: 'solid'
  23. }]"
  24. />
  25. <view class="u-upload__wrap__preview__video"
  26. :style="{
  27. width: addUnit(width),
  28. height: addUnit(height),
  29. backgroundColor: upThemeVar('--up-card-bg-color', 'rgb(242, 242, 242)'),
  30. borderColor: upThemeVar('--up-border-color', '#e5e7eb'),
  31. borderWidth: '1px',
  32. borderStyle: 'solid'
  33. }"
  34. v-else-if="(item.isVideo || (item.type && item.type === 'video')) && getVideoThumb">
  35. <image
  36. v-if="item.thumb"
  37. :src="item.thumb"
  38. :mode="imageMode"
  39. class="u-upload__wrap__preview__image"
  40. @tap="onClickPreview(item, index)"
  41. :style="[{
  42. width: addUnit(width),
  43. height: addUnit(height)
  44. }]"
  45. />
  46. <up-icon
  47. v-else
  48. color="#80CBF9"
  49. size="26"
  50. :name="item.isVideo || (item.type && item.type === 'video') ? 'movie' : 'file-text'"
  51. ></up-icon>
  52. <view v-if="item.status === 'success'"
  53. class="u-upload__wrap__play"
  54. @tap="onClickPreview(item, index)">
  55. <slot name="playIcon"></slot>
  56. <up-icon v-if="!$slots['playIcon']"
  57. class="u-upload__wrap__play__icon"
  58. name="play-right" size="22px"></up-icon>
  59. </view>
  60. </view>
  61. <view
  62. v-else
  63. class="u-upload__wrap__preview__other"
  64. @tap="onClickPreview(item, index)"
  65. :style="[{
  66. width: addUnit(width),
  67. height: addUnit(height),
  68. backgroundColor: upThemeVar('--up-card-bg-color', 'rgb(242, 242, 242)'),
  69. borderColor: upThemeVar('--up-border-color', '#e5e7eb'),
  70. borderWidth: '1px',
  71. borderStyle: 'solid'
  72. }]"
  73. >
  74. <up-icon
  75. color="#80CBF9"
  76. size="26"
  77. :name="item.isVideo || (item.type && item.type === 'video') ? 'movie' : 'folder'"
  78. ></up-icon>
  79. <text class="u-upload__wrap__preview__other__text">
  80. {{item.isVideo || (item.type && item.type === 'video') ? item.name || t("up.common.video") : item.name || t("up.common.file")}}
  81. </text>
  82. </view>
  83. <view
  84. class="u-upload__status"
  85. v-if="item.status === 'uploading' || item.status === 'failed'"
  86. >
  87. <view class="u-upload__status__icon">
  88. <up-icon
  89. v-if="item.status === 'failed'"
  90. name="close-circle"
  91. color="#ffffff"
  92. size="25"
  93. />
  94. <u-loading-icon
  95. size="22"
  96. mode="circle"
  97. v-else
  98. />
  99. </view>
  100. <text
  101. v-if="item.message"
  102. class="u-upload__status__message"
  103. >{{ item.message }}</text>
  104. <up-gap class="u-upload__progress" height="3px"
  105. :style="{width: item.progress + '%'}"></up-gap>
  106. </view>
  107. <view
  108. class="u-upload__deletable"
  109. v-if="item.status !== 'uploading' && (deletable || item.deletable)"
  110. @tap.stop="deleteItem(index)"
  111. >
  112. <view class="u-upload__deletable__icon">
  113. <up-icon
  114. name="close"
  115. color="#ffffff"
  116. size="10"
  117. ></up-icon>
  118. </view>
  119. </view>
  120. <slot name="success">
  121. <view
  122. class="u-upload__success"
  123. v-if="item.status === 'success'"
  124. >
  125. <!-- #ifdef APP-NVUE -->
  126. <image
  127. :src="successIcon"
  128. class="u-upload__success__icon"
  129. ></image>
  130. <!-- #endif -->
  131. <!-- #ifndef APP-NVUE -->
  132. <view class="u-upload__success__icon">
  133. <up-icon
  134. name="checkmark"
  135. color="#ffffff"
  136. size="12"
  137. ></up-icon>
  138. </view>
  139. <!-- #endif -->
  140. </view>
  141. </slot>
  142. </view>
  143. </template>
  144. <up-canvas
  145. ref="videoThumbCanvas"
  146. class="u-upload__hidden-canvas"
  147. :canvas-id="videoThumbCanvasId"
  148. :width="videoThumbCanvasWidth"
  149. :height="videoThumbCanvasHeight"
  150. bg-color="transparent"
  151. ></up-canvas>
  152. <template v-if="isInCount">
  153. <view
  154. v-if="$slots.trigger"
  155. @tap="chooseFile"
  156. >
  157. <slot name="trigger" />
  158. </view>
  159. <view
  160. v-else-if="!$slots.trigger && ($slots.default || $slots.$default)"
  161. @tap="chooseFile"
  162. >
  163. <slot />
  164. </view>
  165. <view
  166. v-else
  167. class="u-upload__button"
  168. :hover-class="!disabled ? 'u-upload__button--hover' : ''"
  169. hover-stay-time="150"
  170. @tap="chooseFile"
  171. :class="[disabled && 'u-upload__button--disabled']"
  172. :style="[{
  173. width: addUnit(width),
  174. height: addUnit(height),
  175. backgroundColor: upThemeVar('--up-card-bg-color', 'rgb(244, 245, 247)'),
  176. borderColor: upThemeVar('--up-border-color', '#e5e7eb'),
  177. borderWidth: '1px',
  178. borderStyle: 'solid'
  179. }]"
  180. >
  181. <up-icon
  182. :name="uploadIcon"
  183. size="26"
  184. :color="resolvedUploadIconColor"
  185. ></up-icon>
  186. <text
  187. v-if="uploadText"
  188. class="u-upload__button__text"
  189. :style="{ color: resolvedUploadTextColor }"
  190. >{{ uploadText }}</text>
  191. </view>
  192. </template>
  193. </view>
  194. <up-popup
  195. mode="center"
  196. v-model:show="popupShow">
  197. <video id="myVideo" v-if="popupShow"
  198. :src="currentItemIndex >= 0 ? lists[currentItemIndex].url : ''"
  199. @error="videoErrorCallback" show-center-play-btn
  200. :object-fit='videoPreviewObjectFit' show-fullscreen-btn='true'
  201. enable-play-gesture controls
  202. :autoplay="true" auto-pause-if-open-native
  203. @loadedmetadata="loadedVideoMetadata"
  204. :initial-time='0.1'>
  205. </video>
  206. </up-popup>
  207. </view>
  208. </template>
  209. <script>
  210. import {
  211. chooseFile
  212. } from './utils';
  213. import { mixinUpload } from './mixin';
  214. import { props } from './props';
  215. import { mpMixin } from '../../libs/mixin/mpMixin';
  216. import { mixin } from '../../libs/mixin/mixin';
  217. import { addStyle, addUnit, toast } from '../../libs/function/index';
  218. import test from '../../libs/function/test';
  219. import { t } from '../../libs/i18n'
  220. /**
  221. * upload 上传
  222. * @description 该组件用于上传图片场景
  223. * @tutorial https://uview-plus.jiangruyi.com/components/upload.html
  224. * @property {String} accept 接受的文件类型, 可选值为all media image file video (默认 'image' )
  225. * @property {String | Array} capture 图片或视频拾取模式,当accept为image类型时设置capture可选额外camera可以直接调起摄像头(默认 ['album', 'camera'] )
  226. * @property {Array} extension 选择文件的后缀名,暂只支持.zip、.png等,不支持application/msword等值
  227. * @property {Boolean} compressed 当accept为video时生效,是否压缩视频,默认为true(默认 true )
  228. * @property {String} camera 当accept为video时生效,可选值为back或front(默认 'back' )
  229. * @property {Number} maxDuration 当accept为video时生效,拍摄视频最长拍摄时间,单位秒(默认 60 )
  230. * @property {String} uploadIcon 上传区域的图标,只能内置图标(默认 'camera-fill' )
  231. * @property {String} uploadIconColor 上传区域的图标的字体颜色,只能内置图标(默认 #D3D4D6 )
  232. * @property {Boolean} useBeforeRead 是否开启文件读取前事件(默认 false )
  233. * @property {Boolean} previewFullImage 是否显示组件自带的图片预览功能(默认 true )
  234. * @property {String | Number} maxCount 最大上传数量(默认 52 )
  235. * @property {Boolean} disabled 是否启用(默认 false )
  236. * @property {String} imageMode 预览上传的图片时的裁剪模式,和image组件mode属性一致(默认 'aspectFill' )
  237. * @property {String} name 标识符,可以在回调函数的第二项参数中获取
  238. * @property {Array} sizeType 所选的图片的尺寸, 可选值为original compressed(默认 ['original', 'compressed'] )
  239. * @property {Boolean} multiple 是否开启图片多选,部分安卓机型不支持 (默认 false )
  240. * @property {Boolean} deletable 是否展示删除按钮(默认 true )
  241. * @property {String | Number} maxSize 文件大小限制,单位为byte (默认 Number.MAX_VALUE )
  242. * @property {Array} fileList 显示已上传的文件列表
  243. * @property {String} uploadText 上传区域的提示文字
  244. * @property {String | Number} width 内部预览图片区域和选择图片按钮的区域宽度(默认 80 )
  245. * @property {String | Number} height 内部预览图片区域和选择图片按钮的区域高度(默认 80 )
  246. * @property {Object} customStyle 组件的样式,对象形式
  247. * @event {Function} afterRead 读取后的处理函数
  248. * @event {Function} beforeRead 读取前的处理函数
  249. * @event {Function} oversize 文件超出大小限制
  250. * @event {Function} clickPreview 点击预览图片
  251. * @event {Function} delete 删除图片
  252. * @example <u-upload :action="action" :fileList="fileList" ></u-upload>
  253. */
  254. export default {
  255. name: "u-upload",
  256. mixins: [mpMixin, mixin, mixinUpload, props],
  257. data() {
  258. return {
  259. // #ifdef APP-NVUE
  260. successIcon: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAKKADAAQAAAABAAAAKAAAAAB65masAAACP0lEQVRYCc3YXygsURwH8K/dpcWyG3LF5u/6/+dKVylSypuUl6uUPMifKMWL8oKEB1EUT1KeUPdR3uTNUsSLxb2udG/cbvInNuvf2rVnazZ/ZndmZ87snjM1Z+Z3zpzfp9+Z5mEAhlvjRtZgCKs+gnPAOcAkkMOR4jEHfItjDvgRxxSQD8cM0BuOCaAvXNCBQrigAsXgggYUiwsK0B9cwIH+4gIKlIILGFAqLiBAOTjFgXJxigJp4BQD0sIpAqSJow6kjSNAFTnRaHJwLenD6Mud52VQAcrBfTd2oyq+HtGaGGWAcnAVcXWoM3bCZrdi+ncPfaAcXE5UKVpdW/vitGPqqAtn98d0gXJwX7Qp6MmegUYVhvmTIezdmHlxJCjpHRTCFerLkRRu4k0aqdajN3sWOo0BK//msHa+xDuPC/oNFMKRhTtM4xjIX0SCNpXL4+7VIaHuyiWEp2L7ahWLf8fejfPdqPmC3mJicORZUp1CQzm+GiphvljGk+PBvWRbxii+xVTj5M6CiZ/tsDufvaXyxEUDxeLIyvu3m0iOyEFWVAkydcVYdyFrE9tQk9iMq6f/GNlvwt3LjQfh60LUrw9/cFyyMJUW/XkLSNMV4Mi6C5ML+ui4x5ClAX9sB9w0wV6wglJwJCv5fOxcr6EstgbGiEw4XcfUry4cWrcEUW8n+ARKxXEJHhw2WG43UKSvwI/TSZgvl7kh0b3XLZaLEy0QmMgLZAVH7J+ALOE+AVnDvQOyiPMAWcW5gSzjCPAV+78S5WE0GrQAAAAASUVORK5CYII=',
  261. // #endif
  262. lists: [],
  263. isInCount: true,
  264. popupShow: false,
  265. currentItemIndex: -1,
  266. videoThumbCanvasId: 'u-upload-video-thumb-' + Math.random().toString(36).substr(2, 9),
  267. videoThumbCanvasWidth: 100,
  268. videoThumbCanvasHeight: 150
  269. }
  270. },
  271. watch: {
  272. // 监听文件列表的变化,重新整理内部数据
  273. fileList: {
  274. handler() {
  275. this.formatFileList()
  276. },
  277. immediate: true,
  278. deep: true,
  279. },
  280. deletable(newVal) {
  281. this.formatFileList()
  282. },
  283. maxCount(newVal) {
  284. this.formatFileList()
  285. },
  286. accept(newVal) {
  287. this.formatFileList()
  288. },
  289. popupShow(newVal) {
  290. if (!newVal) {
  291. this.currentItemIndex = -1;
  292. }
  293. }
  294. },
  295. computed: {
  296. resolvedUploadIconColor() {
  297. if (this.upHasProp('uploadIconColor') || this.uploadIconColor !== '#D3D4D6') {
  298. return this.uploadIconColor
  299. }
  300. return this.upThemeVar('--up-content-color', '#909399')
  301. },
  302. resolvedUploadTextColor() {
  303. return this.upThemeVar('--up-tips-color', '#909399')
  304. }
  305. },
  306. // #ifdef VUE3
  307. emits: ['error', 'beforeRead', 'oversize', 'afterRead', 'delete', 'clickPreview', 'update:fileList', 'afterAutoUpload'],
  308. // #endif
  309. methods: {
  310. t,
  311. addUnit,
  312. addStyle,
  313. videoErrorCallback() {},
  314. loadedVideoMetadata(e) {
  315. if (this.currentItemIndex < 0) {
  316. return;
  317. }
  318. if (this.autoUploadDriver != 'local') {
  319. return;
  320. }
  321. if (!this.getVideoThumb) {
  322. return;
  323. }
  324. // 截取第一帧作为封面,oss等云存储场景直接使用拼接参数。
  325. const w = this.lists[this.currentItemIndex].width;
  326. const h = this.lists[this.currentItemIndex].height;
  327. const dpr = uni.getSystemInfoSync().pixelRatio;
  328. uni.createSelectorQuery().select('#myVideo').context(res => {
  329. console.log('select video', res)
  330. const myVideo = res.context
  331. this.videoThumbCanvasWidth = w
  332. this.videoThumbCanvasHeight = h
  333. this.$nextTick(async () => {
  334. const canvas = this.$refs.videoThumbCanvas
  335. if (!canvas || !myVideo) return
  336. await canvas.initCanvas(true)
  337. const ctx1 = canvas.getRawContext()
  338. if (!ctx1) return
  339. setTimeout(async () => {
  340. try {
  341. ctx1.drawImage(myVideo, 0, 0, w, h)
  342. const result = await canvas.toTempFilePath({
  343. width: w,
  344. height: h,
  345. destWidth: w * dpr,
  346. destHeight: h * dpr
  347. })
  348. console.log('First frame image path:', result.tempFilePath)
  349. this.fileList[this.currentItemIndex].thumb = result.tempFilePath
  350. this.$emit('update:fileList', this.fileList)
  351. } catch (err) {
  352. console.error('Failed to export image:', err)
  353. }
  354. }, 500)
  355. })
  356. }).exec()
  357. },
  358. formatFileList() {
  359. const {
  360. fileList = [], maxCount
  361. } = this;
  362. const lists = fileList.map((item) => {
  363. const name = item.name || item.url || item.thumb
  364. return Object.assign(Object.assign({}, item), {
  365. // 如果item.url为本地选择的blob文件的话,无法判断其为video还是image,此处优先通过accept做判断处理
  366. isImage: item.name ? test.image(item.name) : (this.accept === 'image' || test.image(name)),
  367. isVideo: item.name ? test.video(item.name) : (this.accept === 'video' || test.video(name)),
  368. deletable: typeof item.deletable === 'boolean' ? item.deletable : this.deletable,
  369. })
  370. });
  371. this.lists = lists
  372. this.isInCount = lists.length < maxCount
  373. },
  374. chooseFile(params) {
  375. const {
  376. maxCount,
  377. multiple,
  378. lists,
  379. disabled
  380. } = this;
  381. if (disabled) return Promise.reject();
  382. const chooseParams = Object.assign({
  383. accept: this.accept,
  384. extension: this.extension,
  385. multiple: this.multiple,
  386. capture: this.capture,
  387. compressed: this.compressed,
  388. maxDuration: this.maxDuration,
  389. sizeType: this.sizeType,
  390. camera: this.camera,
  391. }, {
  392. maxCount: maxCount - lists.length,
  393. ...params
  394. })
  395. return chooseFile(chooseParams)
  396. .then((res) => {
  397. const result = chooseParams.multiple ? res : res[0]
  398. this.onBeforeRead(result);
  399. return result
  400. })
  401. .catch((error) => {
  402. this.$emit('error', error);
  403. });
  404. },
  405. // 文件读取之前
  406. onBeforeRead(file) {
  407. const {
  408. beforeRead,
  409. useBeforeRead,
  410. } = this;
  411. let res = file
  412. // beforeRead是否为一个方法
  413. if (test.func(beforeRead)) {
  414. // 如果用户定义了此方法,则去执行此方法,并传入读取的文件回调
  415. res = beforeRead(file, this.getDetail());
  416. }
  417. if (useBeforeRead) {
  418. res = new Promise((resolve, reject) => {
  419. this.$emit(
  420. 'beforeRead',
  421. Object.assign(Object.assign({
  422. file
  423. }, this.getDetail()), {
  424. callback: (ok) => {
  425. ok ? resolve() : reject();
  426. },
  427. })
  428. );
  429. });
  430. }
  431. if (test.promise(res)) {
  432. res.then((data) => this.onAfterRead(data || file));
  433. } else {
  434. this.onAfterRead(res || file);
  435. }
  436. },
  437. getDetail(index) {
  438. return {
  439. name: this.name,
  440. index: index == null ? this.fileList.length : index,
  441. };
  442. },
  443. async onAfterRead(file) {
  444. const {
  445. maxSize,
  446. afterRead
  447. } = this;
  448. const oversize = Array.isArray(file) ?
  449. file.some((item) => item.size > maxSize) :
  450. file.size > maxSize;
  451. if (oversize) {
  452. uni.showToast({
  453. title: t("up.upload.sizeExceed")
  454. })
  455. this.$emit('oversize', Object.assign({
  456. file
  457. }, this.getDetail()));
  458. return;
  459. }
  460. let len = this.fileList.length;
  461. if (this.autoUpload) {
  462. // 当设置 mutiple 为 true 时, file 为数组格式,否则为对象格式
  463. let lists = [].concat(file);
  464. let fileListLen = this.fileList.length;
  465. lists.map((item) => {
  466. this.fileList.push({
  467. ...item,
  468. status: 'uploading',
  469. message: t("up.upload.uploading"),
  470. progress: 0
  471. });
  472. });
  473. let that = this;
  474. this.$emit('update:fileList', this.fileList);
  475. for (let i = 0; i < lists.length; i++) {
  476. let j = i;
  477. let result = '';
  478. switch(this.autoUploadDriver) {
  479. case 'cos': // 腾讯云
  480. break;
  481. case 'kodo': // 七牛云
  482. break;
  483. case 'oss':
  484. case 'upload_oss':
  485. // 阿里云前端直传
  486. // 获取签名
  487. console.log()
  488. let formData = {};
  489. let ret = await uni.request({
  490. url: this.autoUploadAuthUrl,
  491. method: 'get',
  492. header: this.autoUploadHeader,
  493. data: {
  494. filename: lists[j].name
  495. }
  496. });
  497. // console.log(ret);
  498. let res0 = ret.data;
  499. if (res0.code == 200) {
  500. // 路径 + 文件名 + 扩展名
  501. // 不传递filename就要拼接key
  502. // res0.data.params.key = res0.data.params.dir + res0.data.params.uniqidName + fileExt;
  503. formData = res0.data.params;
  504. } else {
  505. uni.showToast({
  506. title: res0.msg,
  507. duration: 1500
  508. });
  509. return;
  510. }
  511. var uploadTask = uni.uploadFile({
  512. url: res0.data.params.host,
  513. filePath: lists[j].url,
  514. name: 'file',
  515. // fileType: 'video', // 仅支付宝小程序,且必填。
  516. // header: header,
  517. formData: formData,
  518. success: (uploadFileRes) => {
  519. let thumb = '';
  520. let afterPromise = '';
  521. if (that.customAfterAutoUpload) {
  522. afterPromise = new Promise((resolve, reject) => {
  523. that.$emit(
  524. 'afterAutoUpload',
  525. Object.assign(res0, {
  526. callback: (r) => {
  527. r.url ? resolve(r) : reject();
  528. },
  529. })
  530. );
  531. });
  532. }
  533. if (test.promise(afterPromise)) {
  534. afterPromise.then((data) => that.succcessUpload(len + j, data.url, data.thumb));
  535. } else {
  536. result = res0.data.params.host + '/' + res0.data.params.key;
  537. if (that.accept === 'video' || test.video(result)) {
  538. thumb = result + '?x-oss-process=video/snapshot,t_10000,m_fast';
  539. }
  540. that.succcessUpload(len + j, result, thumb);
  541. }
  542. }
  543. });
  544. uploadTask.onProgressUpdate((res) => {
  545. that.updateUpload(len + j, {
  546. progress: res.progress
  547. });
  548. // console.log('上传进度' + res.progress);
  549. // console.log('已经上传的数据长度' + res.totalBytesSent);
  550. // console.log('预期需要上传的数据总长度' + res.totalBytesExpectedToSend);
  551. });
  552. break;
  553. case 'local':
  554. default:
  555. // 服务器本机上传
  556. var uploadTask = uni.uploadFile({
  557. url: this.autoUploadApi,
  558. filePath: lists[j].url,
  559. name: 'file',
  560. // fileType: 'video', // 仅支付宝小程序,且必填。
  561. header: this.autoUploadHeader,
  562. success: (uploadFileRes) => {
  563. let res0 = uploadFileRes.data;
  564. let afterPromise = '';
  565. if (that.customAfterAutoUpload) {
  566. afterPromise = new Promise((resolve, reject) => {
  567. that.$emit(
  568. 'afterAutoUpload',
  569. Object.assign(res0, {
  570. callback: (r) => {
  571. r.url ? resolve(r) : reject();
  572. }
  573. })
  574. );
  575. });
  576. }
  577. if (test.promise(afterPromise)) {
  578. afterPromise.then((data) => that.succcessUpload(len + j, data.url));
  579. } else {
  580. if (res0.code != 200) {
  581. uni.showToast({
  582. title: res0.msg
  583. });
  584. } else {
  585. result = res0.data.url;
  586. that.succcessUpload(len + j, result);
  587. }
  588. }
  589. }
  590. });
  591. uploadTask.onProgressUpdate((res) => {
  592. that.updateUpload(len + j, {
  593. progress: res.progress
  594. });
  595. // console.log('上传进度' + res.progress);
  596. // console.log('已经上传的数据长度' + res.totalBytesSent);
  597. // console.log('预期需要上传的数据总长度' + res.totalBytesExpectedToSend);
  598. });
  599. break;
  600. }
  601. }
  602. } else {
  603. if (typeof afterRead === 'function') {
  604. afterRead(file, this.getDetail());
  605. }
  606. this.$emit('afterRead', Object.assign({
  607. file
  608. }, this.getDetail()));
  609. }
  610. },
  611. updateUpload(index, param) {
  612. let item = this.fileList[index];
  613. this.fileList.splice(index, 1, {
  614. ...item,
  615. // 注意这里不判断会出现succcessUpload先执行又被覆盖的问题
  616. status: param.progress == 100 ? 'success' : 'uploading',
  617. message: '',
  618. progress: param.progress
  619. });
  620. this.$emit('update:fileList', this.fileList);
  621. },
  622. succcessUpload(index, url, thumb = '') {
  623. let item = this.fileList[index];
  624. this.fileList.splice(index, 1, {
  625. ...item,
  626. status: 'success',
  627. message: '',
  628. url: url,
  629. progress: 100,
  630. thumb: thumb
  631. });
  632. this.$emit('update:fileList', this.fileList);
  633. },
  634. deleteItem(index) {
  635. if (this.autoDelete) {
  636. this.fileList.splice(index, 1);
  637. this.$emit('update:fileList', this.fileList);
  638. } else {
  639. this.$emit(
  640. 'delete',
  641. Object.assign(Object.assign({}, this.getDetail(index)), {
  642. file: this.fileList[index],
  643. })
  644. );
  645. }
  646. },
  647. // 预览图片
  648. onPreviewImage(previewItem, index) {
  649. if (!previewItem.isImage || !this.previewFullImage) return
  650. let current = 0;
  651. const urls = [];
  652. let imageIndex = 0;
  653. for (var i = 0; i < this.lists.length; i++) {
  654. const item = this.lists[i];
  655. if (item.isImage || (item.type && item.type === 'image')) {
  656. urls.push(item.url || item.thumb);
  657. if (i === index) {
  658. current = imageIndex;
  659. }
  660. imageIndex += 1;
  661. }
  662. }
  663. if (urls.length < 1) {
  664. return;
  665. }
  666. uni.previewImage({
  667. urls: urls,
  668. current: current,
  669. fail() {
  670. toast(t("up.upload.previewImageFail"))
  671. },
  672. });
  673. },
  674. onPreviewVideo(previewItem, index) {
  675. if (!this.previewFullImage) return;
  676. let current = 0;
  677. const sources = [];
  678. let videoIndex = 0;
  679. for (var i = 0; i < this.lists.length; i++) {
  680. const item = this.lists[i];
  681. if (item.isVideo || (item.type && item.type === 'video')) {
  682. sources.push(Object.assign(Object.assign({}, item), {
  683. type: 'video'
  684. }));
  685. if (i === index) {
  686. current = videoIndex;
  687. }
  688. videoIndex += 1;
  689. }
  690. }
  691. if (sources.length < 1) {
  692. return;
  693. }
  694. // #ifndef MP-WEIXIN
  695. this.popupShow = true;
  696. this.currentItemIndex = index;
  697. console.log(this.lists[this.currentItemIndex])
  698. // #endif
  699. // #ifdef MP-WEIXIN
  700. wx.previewMedia({
  701. sources: sources,
  702. current: current,
  703. fail() {
  704. toast(t("up.upload.previewVideoFail"))
  705. },
  706. });
  707. // #endif
  708. },
  709. onClickPreview(item, index) {
  710. if (this.previewFullImage) {
  711. switch (item.type) {
  712. case 'image':
  713. this.onPreviewImage(item, index);
  714. break;
  715. case 'video':
  716. this.onPreviewVideo(item, index);
  717. break;
  718. default:
  719. break;
  720. }
  721. }
  722. this.$emit(
  723. 'clickPreview',
  724. Object.assign(Object.assign({}, item), this.getDetail(index))
  725. );
  726. }
  727. }
  728. }
  729. </script>
  730. <style lang="scss" scoped>
  731. $u-upload-preview-border-radius: 2px !default;
  732. $u-upload-preview-margin: 0 8px 8px 0 !default;
  733. $u-upload-image-width:80px !default;
  734. $u-upload-image-height:$u-upload-image-width;
  735. $u-upload-other-bgColor: rgb(242, 242, 242) !default;
  736. $u-upload-other-flex:1 !default;
  737. $u-upload-text-font-size:11px !default;
  738. $u-upload-text-color:$u-tips-color !default;
  739. $u-upload-text-margin-top:2px !default;
  740. $u-upload-deletable-right:0 !default;
  741. $u-upload-deletable-top:0 !default;
  742. $u-upload-deletable-bgColor:rgb(55, 55, 55) !default;
  743. $u-upload-deletable-height:14px !default;
  744. $u-upload-deletable-width:$u-upload-deletable-height;
  745. $u-upload-deletable-boder-bottom-left-radius:100px !default;
  746. $u-upload-deletable-zIndex:3 !default;
  747. $u-upload-success-bottom:0 !default;
  748. $u-upload-success-right:0 !default;
  749. $u-upload-success-border-style:solid !default;
  750. $u-upload-success-border-top-color:transparent !default;
  751. $u-upload-success-border-left-color:transparent !default;
  752. $u-upload-success-border-bottom-color: $u-success !default;
  753. $u-upload-success-border-right-color:$u-upload-success-border-bottom-color;
  754. $u-upload-success-border-width:9px !default;
  755. $u-upload-icon-top:0px !default;
  756. $u-upload-icon-right:0px !default;
  757. $u-upload-icon-h5-top:1px !default;
  758. $u-upload-icon-h5-right:0 !default;
  759. $u-upload-icon-width:16px !default;
  760. $u-upload-icon-height:$u-upload-icon-width;
  761. $u-upload-success-icon-bottom:-10px !default;
  762. $u-upload-success-icon-right:-10px !default;
  763. $u-upload-status-right:0 !default;
  764. $u-upload-status-left:0 !default;
  765. $u-upload-status-bottom:0 !default;
  766. $u-upload-status-top:0 !default;
  767. $u-upload-status-bgColor:rgba(0, 0, 0, 0.5) !default;
  768. $u-upload-status-icon-Zindex:1 !default;
  769. $u-upload-message-font-size:12px !default;
  770. $u-upload-message-color:#FFFFFF !default;
  771. $u-upload-message-margin-top:5px !default;
  772. $u-upload-button-width:80px !default;
  773. $u-upload-button-height:$u-upload-button-width;
  774. $u-upload-button-bgColor:rgb(244, 245, 247) !default;
  775. $u-upload-button-border-radius:2px !default;
  776. $u-upload-botton-margin: 0 8px 8px 0 !default;
  777. $u-upload-text-font-size:11px !default;
  778. $u-upload-text-color:$u-tips-color !default;
  779. $u-upload-text-margin-top: 2px !default;
  780. $u-upload-hover-bgColor:rgb(230, 231, 233) !default;
  781. $u-upload-disabled-opacity:.5 !default;
  782. .u-upload {
  783. @include flex(column);
  784. flex: 1;
  785. &__wrap {
  786. @include flex;
  787. flex-wrap: wrap;
  788. flex: 1;
  789. &__preview {
  790. border-radius: $u-upload-preview-border-radius;
  791. margin: $u-upload-preview-margin;
  792. position: relative;
  793. overflow: hidden;
  794. @include flex;
  795. &__image {
  796. width: $u-upload-image-width;
  797. height: $u-upload-image-height;
  798. }
  799. &__video,
  800. &__other {
  801. width: $u-upload-image-width;
  802. height: $u-upload-image-height;
  803. background-color: $u-upload-other-bgColor;
  804. flex: $u-upload-other-flex;
  805. @include flex(column);
  806. justify-content: center;
  807. align-items: center;
  808. &__text {
  809. font-size: $u-upload-text-font-size;
  810. color: $u-upload-text-color;
  811. margin-top: $u-upload-text-margin-top;
  812. }
  813. }
  814. }
  815. }
  816. &__hidden-canvas {
  817. position: fixed;
  818. top: -10000px;
  819. left: -10000px;
  820. z-index: -1;
  821. }
  822. &__wrap__play {
  823. position: absolute;
  824. top: 0px;
  825. left: 0px;
  826. bottom: 0px;
  827. right: 0px;
  828. display: flex;
  829. justify-content: center;
  830. align-items: center;
  831. &__icon {
  832. background: #fff;
  833. border-radius: 100px;
  834. opacity: 0.8;
  835. };
  836. }
  837. &__deletable {
  838. position: absolute;
  839. top: $u-upload-deletable-top;
  840. right: $u-upload-deletable-right;
  841. background-color: $u-upload-deletable-bgColor;
  842. height: $u-upload-deletable-height;
  843. width: $u-upload-deletable-width;
  844. @include flex;
  845. border-bottom-left-radius: $u-upload-deletable-boder-bottom-left-radius;
  846. align-items: center;
  847. justify-content: center;
  848. z-index: $u-upload-deletable-zIndex;
  849. &__icon {
  850. position: absolute;
  851. transform: scale(0.7);
  852. top: $u-upload-icon-top;
  853. right: $u-upload-icon-right;
  854. /* #ifdef H5 */
  855. top: $u-upload-icon-h5-top;
  856. right: $u-upload-icon-h5-right;
  857. /* #endif */
  858. }
  859. }
  860. &__success {
  861. position: absolute;
  862. bottom: $u-upload-success-bottom;
  863. right: $u-upload-success-right;
  864. @include flex;
  865. // 由于weex(nvue)为阿里巴巴的KPI(部门业绩考核)的laji产物,不支持css绘制三角形
  866. // 所以在nvue下使用图片,非nvue下使用css实现
  867. /* #ifndef APP-NVUE */
  868. border-style: $u-upload-success-border-style;
  869. border-top-color: $u-upload-success-border-top-color;
  870. border-left-color: $u-upload-success-border-left-color;
  871. border-bottom-color: $u-upload-success-border-bottom-color;
  872. border-right-color: $u-upload-success-border-right-color;
  873. border-width: $u-upload-success-border-width;
  874. align-items: center;
  875. justify-content: center;
  876. /* #endif */
  877. &__icon {
  878. /* #ifndef APP-NVUE */
  879. position: absolute;
  880. transform: scale(0.7);
  881. bottom: $u-upload-success-icon-bottom;
  882. right: $u-upload-success-icon-right;
  883. /* #endif */
  884. /* #ifdef APP-NVUE */
  885. width: $u-upload-icon-width;
  886. height: $u-upload-icon-height;
  887. /* #endif */
  888. }
  889. }
  890. &__progress {
  891. background-color: $u-primary !important;
  892. position: absolute;
  893. bottom: 0;
  894. left: 0;
  895. }
  896. &__status {
  897. position: absolute;
  898. top: $u-upload-status-top;
  899. bottom: $u-upload-status-bottom;
  900. left: $u-upload-status-left;
  901. right: $u-upload-status-right;
  902. background-color: $u-upload-status-bgColor;
  903. @include flex(column);
  904. align-items: center;
  905. justify-content: center;
  906. &__icon {
  907. position: relative;
  908. z-index: $u-upload-status-icon-Zindex;
  909. }
  910. &__message {
  911. font-size: $u-upload-message-font-size;
  912. color: $u-upload-message-color;
  913. margin-top: $u-upload-message-margin-top;
  914. }
  915. }
  916. &__button {
  917. @include flex(column);
  918. align-items: center;
  919. justify-content: center;
  920. width: $u-upload-button-width;
  921. height: $u-upload-button-height;
  922. background-color: $u-upload-button-bgColor;
  923. border-radius: $u-upload-button-border-radius;
  924. margin: $u-upload-botton-margin;
  925. /* #ifndef APP-NVUE */
  926. box-sizing: border-box;
  927. /* #endif */
  928. &__text {
  929. font-size: $u-upload-text-font-size;
  930. color: $u-upload-text-color;
  931. margin-top: $u-upload-text-margin-top;
  932. }
  933. &--hover {
  934. background-color: $u-upload-hover-bgColor;
  935. }
  936. &--disabled {
  937. opacity: $u-upload-disabled-opacity;
  938. }
  939. }
  940. }
  941. </style>