浏览代码

feat(uploader): 微信小程序支持上传视频文件、录制时长等功能 #2119

richard1015 2 年之前
父节点
当前提交
1978e6a9cc

+ 29 - 22
src/packages/__VUE/uploader/doc.taro.md

@@ -58,6 +58,11 @@ app.use(Uploader);
 ``` html
 <nut-uploader :url="uploadUrl" :source-type="['camera']" ></nut-uploader>
 ```
+### 使用前摄像头拍摄3s视频并上传(仅支持微信小程序)
+    
+``` html
+<nut-uploader max-duration="3" :source-type="['camera']" camera="front" :url="uploadUrl"></nut-uploader>
+```
 
 ### 限制上传数量5个
 
@@ -228,28 +233,30 @@ setup() {
 ## API
 ### Props
 
-| 参数              | 说明                                                                                                             | 类型                              | 默认值                      |
-|-------------------|------------------------------------------------------------------------------------------------------------------|-----------------------------------|-----------------------------|
-| auto-upload       | 是否在选取文件后立即进行上传,`false` 时需要手动执行 ref submit 方法进行上传                                       | boolean                           | `true`                      |
-| name              | 发到后台的文件参数名                                                                                             | string                            | `file`                      |
-| url               | 上传服务器的接口地址                                                                                             | string                            | `-`                         |
-| v-model:file-list | 默认已经上传的文件列表                                                                                           | FileItem[]                        | `[]`                        |
-| is-preview        | 是否上传成功后展示预览图                                                                                         | boolean                           | `true`                      |
-| is-deletable      | 是否展示删除按钮                                                                                                 | boolean                           | `true`                      |
-| method            | 上传请求的 http method                                                                                           | string                            | `post`                      |
-| list-type         | 上传列表的内建样式,支持两种基础样式 `picture`、`list`                                                           | string                            | `picture`                   |
-| source-type       | [选择图片的来源](https://developers.weixin.qq.com/miniprogram/dev/api/media/image/wx.chooseImage.html)           | string                            | `['album','camera']`        |
-| maximize          | 可以设定最大上传文件的大小(字节)                                                                               | number \| string                  | `9`                         |
-| maximum           | 文件上传数量限制                                                                                                 | number \| string                  | `1`                         |
-| size-type         | 是否压缩所选文件[详细说明](https://developers.weixin.qq.com/miniprogram/dev/api/media/image/wx.chooseImage.html) | string                            | `['original','compressed']` |
-| headers           | 设置上传的请求头部                                                                                               | object                            | `{}`                        |
-| data              | 附加上传的信息 formData                                                                                          | object                            | `{}`                        |
-| xhr-state         | 接口响应的成功状态(status)值                                                                                   | number                            | `200`                       |
-| disabled          | 是否禁用文件上传                                                                                                 | boolean                           | `false`                     |
-| multiple          | 是否支持文件多选                                                                                                 | boolean                           | `true`                      |
-| timeout           | 超时时间,单位为毫秒                                                                                             | number \| string                  | `1000 * 30`                 |
-| before-upload     | 上传前的函数需要返回一个`Promise`对象                                                                            | Function                          | `null`                      |
-| before-xhr-upload | 执行 Taro.uploadFile 上传时,自定义方式                                                                          | Function(Taro.uploadFile,option) | `null`                      |
+| 参数                      | 说明                                                                                                     | 类型                              | 默认值                      |
+|---------------------------|----------------------------------------------------------------------------------------------------------|-----------------------------------|-----------------------------|
+| auto-upload               | 是否在选取文件后立即进行上传,`false` 时需要手动执行 ref submit 方法进行上传                             | Boolean                           | `true`                      |
+| name                      | 发到后台的文件参数名                                                                                     | String                            | `file`                      |
+| url                       | 上传服务器的接口地址                                                                                     | String                            | `-`                         |
+| v-model:file-list         | 默认已经上传的文件列表                                                                                   | FileItem[]                        | `[]`                        |
+| is-preview                | 是否上传成功后展示预览图                                                                                 | Boolean                           | `true`                      |
+| is-deletable              | 是否展示删除按钮                                                                                         | Boolean                           | `true`                      |
+| method                    | 上传请求的 http method                                                                                   | String                            | `post`                      |
+| list-type                 | 上传列表的内建样式,支持两种基础样式 `picture`、`list`                                                   | String                            | `picture`                   |
+| maximize                  | 可以设定最大上传文件的大小(字节)                                                                       | Number \| String                  | `Number.MAX_VALUE`          |
+| maximum                   | 最多可以选择的文件个数,微信基础库2.25.0前,最多可支持9个文件,2.25.0及以后最多可支持20个文件            | Number \| String                  | `1`                         |
+| source-type               | [选择文件的来源](https://developers.weixin.qq.com/miniprogram/dev/api/media/video/wx.chooseMedia.html)   | Array                             | `['album','camera']`        |
+| camera`仅支持WEAPP`       | 仅在 `source-type` 为 `camera` 时生效,使用前置或后置摄像头                                               | String                            | `back`                      |
+| size-type                 | [是否压缩所选文件](https://developers.weixin.qq.com/miniprogram/dev/api/media/video/wx.chooseMedia.html) | Array                             | `['original','compressed']` |
+| media-type`仅支持WEAPP`   | [选择文件类型](https://developers.weixin.qq.com/miniprogram/dev/api/media/video/wx.chooseMedia.html)     | Array                             | `['image', 'video', 'mix']`        |
+| max-duration`仅支持WEAPP` | 拍摄视频最长拍摄时间,单位秒。时间范围为 3s 至 60s 之间。不限制相册。                                    | Number                            | 10                          |
+| headers                   | 设置上传的请求头部                                                                                       | object                            | `{}`                        |
+| data                      | 附加上传的信息 formData                                                                                  | object                            | `{}`                        |
+| xhr-state                 | 接口响应的成功状态(status)值                                                                           | Number                            | `200`                       |
+| disabled                  | 是否禁用文件上传                                                                                         | Boolean                           | `false`                     |
+| multiple                  | 是否支持文件多选                                                                                         | Boolean                           | `true`                      |
+| timeout                   | 超时时间,单位为毫秒                                                                                     | Number \| String                  | `1000 * 30`                 |
+| before-xhr-upload         | 执行 `Taro.uploadFile` 上传时,自定义方式                                                                | Function(Taro.uploadFile,option) | `null`                      |
 
 
 ### FileItem

+ 90 - 48
src/packages/__VUE/uploader/index.taro.vue

@@ -25,7 +25,7 @@
           class="nut-uploader__preview-img__c"
           mode="aspectFit"
           @click="fileItemClick(item)"
-          v-if="item.type?.includes('image') && item.url"
+          v-if="['image','video'].includes(item.type as string) && item.url"
           :src="item.url"
         />
         <view v-else class="nut-uploader__preview-img__file">
@@ -74,7 +74,7 @@
 import { computed, PropType, reactive } from 'vue';
 import { createComponent } from '@/packages/utils/create';
 import { UploaderTaro, UploadOptions } from './uploader';
-import { FileItem, SizeType, SourceType } from './type';
+import { FileItem, MediaType, SizeType, SourceType } from './type';
 import { funInterceptor, Interceptor } from '@/packages/utils/util';
 import Progress from '../progress/index.taro.vue';
 import Button from '../button/index.taro.vue';
@@ -102,6 +102,15 @@ export default create({
       type: Array as PropType<SourceType[]>,
       default: () => ['album', 'camera']
     },
+    mediaType: {
+      type: Array as PropType<MediaType[]>,
+      default: () => ['image', 'video', 'mix']
+    },
+    camera: {
+      type: String,
+      default: 'back'
+    },
+
     timeout: { type: [Number, String], default: 1000 * 30 },
     // defaultFileList: { type: Array, default: () => new Array<FileItem>() },
     fileList: { type: Array, default: () => [] },
@@ -121,10 +130,7 @@ export default create({
     multiple: { type: Boolean, default: true },
     disabled: { type: Boolean, default: false },
     autoUpload: { type: Boolean, default: true },
-    beforeUpload: {
-      type: Function,
-      default: null
-    },
+    maxDuration: { type: Number, default: 10 },
     beforeXhrUpload: {
       type: Function,
       default: null
@@ -177,17 +183,59 @@ export default create({
           document.body.appendChild(obj);
         }
       }
+      if (Taro.getEnv() == 'WEAPP') {
+        // chooseMedia 目前只支持微信小程序原生,其余端全部使用 chooseImage API
+        Taro.chooseMedia({
+          /** 最多可以选择的文件个数 */
+          count: props.multiple ? (props.maximum as number) * 1 - props.fileList.length : 1,
+          /** 文件类型 */
+          mediaType: props.mediaType,
+          /** 图片和视频选择的来源 */
+          sourceType: props.sourceType,
+          /** 拍摄视频最长拍摄时间,单位秒。时间范围为 3s 至 30s 之间 */
+          maxDuration: props.maxDuration,
+          /** 仅对 mediaType 为 image 时有效,是否压缩所选文件 */
+          sizeType: [],
+          /** 仅在 sourceType 为 camera 时生效,使用前置或后置摄像头 */
+          camera: props.camera,
+          /** 接口调用失败的回调函数 */
+          fail: (res: TaroGeneral.CallbackResult) => {
+            emit('failure', res);
+          },
+          /** 接口调用成功的回调函数 */
+          success: onChangeMedia
+        });
+      } else {
+        Taro.chooseImage({
+          // 选择数量
+          count: props.multiple ? (props.maximum as number) * 1 - props.fileList.length : 1,
+          // 可以指定是原图还是压缩图,默认二者都有
+          sizeType: props.sizeType,
+          sourceType: props.sourceType,
+          success: onChangeImage,
+          fail: (res: any) => {
+            emit('failure', res);
+          }
+        });
+      }
+    };
 
-      Taro.chooseImage({
-        // 选择数量
-        count: props.multiple ? (props.maximum as number) * 1 - props.fileList.length : 1,
-        // 可以指定是原图还是压缩图,默认二者都有
-        sizeType: props.sizeType,
-        sourceType: props.sourceType,
-        success: onChange,
-        fail: (res: any) => {
-          emit('failure', res);
-        }
+    const onChangeMedia = (res: Taro.chooseMedia.SuccessCallbackResult) => {
+      // 返回选定照片的本地文件路径列表,tempFilePath可以作为img标签的src属性显示图片
+      const { type, tempFiles } = res;
+      const _files: Taro.chooseMedia.ChooseMedia[] = filterFiles<Taro.chooseMedia.ChooseMedia>(tempFiles);
+      readFile<Taro.chooseMedia.ChooseMedia>(_files);
+      emit('change', {
+        fileList
+      });
+    };
+    const onChangeImage = (res: Taro.chooseImage.SuccessCallbackResult) => {
+      // 返回选定照片的本地文件路径列表,tempFilePath可以作为img标签的src属性显示图片
+      const { tempFilePaths, tempFiles } = res;
+      const _files: Taro.chooseImage.ImageFile[] = filterFiles<Taro.chooseImage.ImageFile>(tempFiles);
+      readFile<Taro.chooseImage.ImageFile>(_files);
+      emit('change', {
+        fileList
       });
     };
 
@@ -267,17 +315,31 @@ export default create({
         res.forEach((i) => i.uploadTaro(Taro.uploadFile, Taro.getEnv()));
       });
     };
-
-    const readFile = (files: Taro.chooseImage.ImageFile[]) => {
-      const imgReg = /\.(png|jpeg|jpg|webp|gif)$/i;
-      files.forEach((file: Taro.chooseImage.ImageFile, index: number) => {
+    interface TFileType {
+      size: number;
+      type?: string;
+      fileType?: string;
+      originalFileObj?: any;
+      tempFilePath?: string;
+      thumbTempFilePath?: string;
+      path?: string;
+    }
+    const readFile = <T extends TFileType>(files: T[]) => {
+      files.forEach((file: T, index: number) => {
         let fileType = file.type;
+        let filepath = (file.tempFilePath || file.path) as string;
         const fileItem = reactive(new FileItem());
-        if (!fileType && (imgReg.test(file.path) || file.path.includes('data:image'))) {
-          fileType = 'image';
+        if (file.fileType) {
+          fileType = file.fileType;
+        } else {
+          const imgReg = /\.(png|jpeg|jpg|webp|gif)$/i;
+          if (!fileType && (imgReg.test(filepath) || filepath.includes('data:image'))) {
+            fileType = 'image';
+          }
         }
-        fileItem.path = file.path;
-        fileItem.name = file.path;
+
+        fileItem.path = filepath;
+        fileItem.name = filepath;
         fileItem.status = 'ready';
         fileItem.message = translate('waitingUpload');
         fileItem.type = fileType;
@@ -294,18 +356,18 @@ export default create({
           fileItem.formData = props.data;
         }
         if (props.isPreview) {
-          fileItem.url = file.path;
+          fileItem.url = fileType == 'video' ? file.thumbTempFilePath : filepath;
         }
         fileList.push(fileItem);
         executeUpload(fileItem, index);
       });
     };
 
-    const filterFiles = (files: Taro.chooseImage.ImageFile[]) => {
+    const filterFiles = <T extends TFileType>(files: T[]) => {
       const maximum = (props.maximum as number) * 1;
       const maximize = (props.maximize as number) * 1;
-      const oversizes = new Array<Taro.chooseImage.ImageFile>();
-      files = files.filter((file: Taro.chooseImage.ImageFile) => {
+      const oversizes = new Array<T>();
+      files = files.filter((file: T) => {
         if (file.size > maximize) {
           oversizes.push(file);
           return false;
@@ -340,26 +402,6 @@ export default create({
       });
     };
 
-    const onChange = (res: Taro.chooseImage.SuccessCallbackResult) => {
-      // 返回选定照片的本地文件路径列表,tempFilePath可以作为img标签的src属性显示图片
-      const { tempFilePaths, tempFiles } = res;
-
-      if (props.beforeUpload) {
-        props.beforeUpload(tempFiles).then((f: Array<Taro.chooseImage.ImageFile>) => changeReadFile(f));
-      } else {
-        changeReadFile(tempFiles);
-      }
-
-      emit('change', {
-        fileList
-      });
-    };
-
-    const changeReadFile = (f: any) => {
-      const _files: Taro.chooseImage.ImageFile[] = filterFiles(f);
-      readFile(_files);
-    };
-
     return {
       onDelete,
       fileList,

+ 2 - 1
src/packages/__VUE/uploader/type.ts

@@ -1,7 +1,8 @@
 import { createComponent } from '@/packages/utils/create';
 const { translate } = createComponent('uploader');
 export type SizeType = 'original' | 'compressed';
-export type SourceType = 'album' | 'camera' | 'user' | 'environment';
+export type SourceType = 'album' | 'camera';
+export type MediaType = 'image' | 'video' | 'mix';
 export type FileItemStatus = 'ready' | 'uploading' | 'success' | 'error';
 export class FileItem {
   status: FileItemStatus = 'ready';

+ 2 - 1
src/sites/mobile-taro/vue/src/dentry/pages/uploader/index.vue

@@ -28,11 +28,12 @@
     <!-- 
       album 从相册选图
       camera 使用相机
-      user 使用前置摄像头(仅H5纯浏览器使用)
       environment 使用后置摄像头(仅H5纯浏览器)
       -->
     <h2>直接调起摄像头 camera </h2>
     <nut-uploader :source-type="['camera']"></nut-uploader>
+    <h2>使用前摄像头拍摄3s视频并上传(仅支持微信小程序)</h2>
+    <nut-uploader max-duration="3" :source-type="['camera']" camera="front" :url="uploadUrl"></nut-uploader>
     <h2>上传状态</h2>
     <nut-uploader :url="uploadUrl" @delete="onDelete"></nut-uploader>
     <h2>限制上传数量5个</h2>