|
@@ -0,0 +1,981 @@
|
|
|
|
+<template>
|
|
|
|
+ <view class="cl-updata">
|
|
|
|
+ <view class="file-list" :style="[listRowStyle]">
|
|
|
|
+
|
|
|
|
+ <view v-for="(item, index) in previewList" @tap="onClickRow(item, index)" class="file-list-row"
|
|
|
|
+ :style="[rowStyle]" :key="index">
|
|
|
|
+
|
|
|
|
+ <image v-if="fileUrlType(item) === 'image'" :src="item.path || item" :style="[imgStyle]"
|
|
|
|
+ mode="aspectFill">
|
|
|
|
+ </image>
|
|
|
|
+
|
|
|
|
+ <view v-else class="_video" :style="[imgStyle]">
|
|
|
|
+
|
|
|
|
+ <!-- #ifdef MP-WEIXIN || MP-ALIPAY -->
|
|
|
|
+ <video v-if="!autoUpload || cloudType === 'other'" class="_video" :style="[imgStyle]"
|
|
|
|
+ :src="item.url || item" :show-center-play-btn="false" :show-fullscreen-btn="false"
|
|
|
|
+ :show-play-btn="false" :show-loading="false" :enable-progress-gesture="false" :controls="false">
|
|
|
|
+ <!-- <cover-view class="video-fixed" ></cover-view> -->
|
|
|
|
+ <cover-view @tap="onPlay(item, index)" class="play">
|
|
|
|
+ <image style="width: 100%;" :src="playImg" mode="widthFix"></image>
|
|
|
|
+ </cover-view>
|
|
|
|
+ </video>
|
|
|
|
+
|
|
|
|
+ <!-- #endif -->
|
|
|
|
+
|
|
|
|
+ <!-- #ifdef APP-PLUS -->
|
|
|
|
+ <video v-if="cloudType === 'other'" class="_video" :style="[imgStyle]" :src="item" :poster="item"
|
|
|
|
+ :controls="false" :show-center-play-btn="false" :show-fullscreen-btn="false"
|
|
|
|
+ :show-play-btn="false" :show-loading="false" :enable-progress-gesture="false">
|
|
|
|
+ <cover-image class="app_play" :src="playImg" @tap="onPlay(item, index)"></cover-image>
|
|
|
|
+ <cover-view class="remove" v-if="remove" @tap="onRemove(item, index)">
|
|
|
|
+ <cover-image class="image" :src="deleteImg" mode="widthFix"
|
|
|
|
+ @tap="onRemove(item, index)"></cover-image>
|
|
|
|
+ </cover-view>
|
|
|
|
+ </video>
|
|
|
|
+ <!-- #endif -->
|
|
|
|
+
|
|
|
|
+ <!-- #ifndef MP-WEIXIN || MP-ALIPAY || APP-PLUS -->
|
|
|
|
+ <video v-if="cloudType === 'other'" class="_video" :autoplay="false" :style="[imgStyle]" :src="item"
|
|
|
|
+ :controls="false" :show-center-play-btn="false" :show-fullscreen-btn="false"
|
|
|
|
+ :show-play-btn="false" :show-loading="false" :enable-progress-gesture="false">
|
|
|
|
+ <!-- <cover-view class="video-fixed" @tap="onPlay(item, index)"></cover-view> -->
|
|
|
|
+ <cover-view @tap="onPlay(item, index)" class="play">
|
|
|
|
+ <image style="width: 100%;" :src="playImg" mode="widthFix"></image>
|
|
|
|
+ </cover-view>
|
|
|
|
+ </video>
|
|
|
|
+
|
|
|
|
+ <!-- #endif -->
|
|
|
|
+
|
|
|
|
+ <cl-image v-else class="pay" :style="[imgStyle]" :cloudType="cloudType"
|
|
|
|
+ :src="(item.path || item)"></cl-image>
|
|
|
|
+
|
|
|
|
+ <view class="play" @tap="onPlay(item, index)">
|
|
|
|
+ <image class="play-img" :src="playImg" mode="widthFix"></image>
|
|
|
|
+ </view>
|
|
|
|
+
|
|
|
|
+ </view>
|
|
|
|
+
|
|
|
|
+ <view class="remove" v-if="remove" @tap.stop="onRemove(item, index)">
|
|
|
|
+ <image class="image" :src="deleteImg" mode="widthFix"></image>
|
|
|
|
+ </view>
|
|
|
|
+ </view>
|
|
|
|
+
|
|
|
|
+ <view v-if="add && FileList.length < max" @tap="onClickAdd" :style="[rowStyle]"
|
|
|
|
+ class="file-list-row add-image">
|
|
|
|
+ <image :src="addImg" mode="widthFix"></image>
|
|
|
|
+ </view>
|
|
|
|
+ </view>
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ <view v-if="tempVideoUrl" class="mask">
|
|
|
|
+ <image @tap="tempVideoUrl = ''" class="_root" :src="closeImg" mode="widthFix"></image>
|
|
|
|
+
|
|
|
|
+ <view class="block" @tap.stop>
|
|
|
|
+ <video autoplay :src="tempVideoUrl"></video>
|
|
|
|
+ </view>
|
|
|
|
+ </view>
|
|
|
|
+ </view>
|
|
|
|
+</template>
|
|
|
|
+
|
|
|
|
+<script>
|
|
|
|
+ import uploadImage from '@/components/ossutil/uploadFile.js';
|
|
|
|
+ import ClImage from '../cl-image/cl-image.vue'
|
|
|
|
+ export default {
|
|
|
|
+ name: "cl-upload",
|
|
|
|
+ components: {
|
|
|
|
+ ClImage
|
|
|
|
+ },
|
|
|
|
+ props: {
|
|
|
|
+ //受控图片列表
|
|
|
|
+ // #ifdef VUE2
|
|
|
|
+ value: {
|
|
|
|
+ type: Array,
|
|
|
|
+ default: () => [],
|
|
|
|
+ },
|
|
|
|
+ // #endif
|
|
|
|
+
|
|
|
|
+ // #ifdef VUE3
|
|
|
|
+ modelValue: {
|
|
|
|
+ type: Array,
|
|
|
|
+ default: () => [],
|
|
|
|
+ },
|
|
|
|
+ // #endif
|
|
|
|
+
|
|
|
|
+ // 存储云类型 oss阿里云 vframe七牛云 process腾讯云 other其他
|
|
|
|
+ cloudType: {
|
|
|
|
+ type: String,
|
|
|
|
+ default: 'oss'
|
|
|
|
+ },
|
|
|
|
+ // 标识符,即后端接口参数名
|
|
|
|
+ fileName: {
|
|
|
|
+ type: String,
|
|
|
|
+ default: 'file'
|
|
|
|
+ },
|
|
|
|
+ // // 文件类型 'image', 'video', 'all'
|
|
|
|
+ // fileType: {
|
|
|
|
+ // type: String,
|
|
|
|
+ // default: 'all'
|
|
|
|
+ // },
|
|
|
|
+ // 上传图片参数
|
|
|
|
+ imageFormData: {
|
|
|
|
+ type: Object,
|
|
|
|
+ default: () => {}
|
|
|
|
+ },
|
|
|
|
+ // 上传视频参数
|
|
|
|
+ videoFromData: {
|
|
|
|
+ type: Object,
|
|
|
|
+ default: () => {}
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ // 必选参数,上传的地址
|
|
|
|
+ action: {
|
|
|
|
+ type: String,
|
|
|
|
+ default: ''
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ // 设置上传的请求头部
|
|
|
|
+ headers: {
|
|
|
|
+ type: Object,
|
|
|
|
+ default: () => {}
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ // 上传时附带的额外参数
|
|
|
|
+ data: {
|
|
|
|
+ type: Object,
|
|
|
|
+ default: () => {}
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ // 是否开启预览图片
|
|
|
|
+ isPreviewImage: {
|
|
|
|
+ type: Boolean,
|
|
|
|
+ default: true
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ // 图片指示器样式,可取值:"default" - 底部圆点指示器; "number" - 顶部数字指示器; "none" - 不显示指示器。
|
|
|
|
+ indicator: {
|
|
|
|
+ type: String,
|
|
|
|
+ default: 'none'
|
|
|
|
+ },
|
|
|
|
+ // 是否在选取文件后立即进行上传
|
|
|
|
+ autoUpload: {
|
|
|
|
+ type: Boolean,
|
|
|
|
+ default: true
|
|
|
|
+ },
|
|
|
|
+ // 是否显示删除按钮
|
|
|
|
+ remove: {
|
|
|
|
+ type: Boolean,
|
|
|
|
+ default: true
|
|
|
|
+ },
|
|
|
|
+ // 是否添加按钮
|
|
|
|
+ // add: {
|
|
|
|
+ // type: Boolean,
|
|
|
|
+ // default: true
|
|
|
|
+ // },
|
|
|
|
+ // 最多显示数量
|
|
|
|
+ max: {
|
|
|
|
+ type: Number,
|
|
|
|
+ default: 9
|
|
|
|
+ },
|
|
|
|
+ // 视频最大上传数量
|
|
|
|
+ maxVideo: {
|
|
|
|
+ type: Number,
|
|
|
|
+ default: 0
|
|
|
|
+ },
|
|
|
|
+ // 列表样式
|
|
|
|
+ listStyle: {
|
|
|
|
+ type: Object,
|
|
|
|
+ default: () => {}
|
|
|
|
+ },
|
|
|
|
+ // 删除提示弹窗标题
|
|
|
|
+ deleteTitle: {
|
|
|
|
+ type: String,
|
|
|
|
+ default: '提示'
|
|
|
|
+ },
|
|
|
|
+ // 删除提示弹窗文案
|
|
|
|
+ deleteText: {
|
|
|
|
+ type: String,
|
|
|
|
+ default: '您确认要删除吗?'
|
|
|
|
+ },
|
|
|
|
+ // 是否开启删除前钩子
|
|
|
|
+ useBeforeDelete: {
|
|
|
|
+ type: Boolean,
|
|
|
|
+ default: false
|
|
|
|
+ },
|
|
|
|
+ // 是否开启上传前钩子
|
|
|
|
+ useBeforeUpload: {
|
|
|
|
+ type: Boolean,
|
|
|
|
+ default: false
|
|
|
|
+ },
|
|
|
|
+ // 添加按钮图片
|
|
|
|
+ addImg: {
|
|
|
|
+ type: String,
|
|
|
|
+ default: 'https://mp-61599c79-d7ee-4a75-a24b-e5a288da6dd3.cdn.bspapp.com/cloudstorage/bb1550b3-e0a8-4a90-a86f-00f8c6afa9fb.png'
|
|
|
|
+ },
|
|
|
|
+ // 播放按钮图片
|
|
|
|
+ playImg: {
|
|
|
|
+ type: String,
|
|
|
|
+ default: 'https://mp-61599c79-d7ee-4a75-a24b-e5a288da6dd3.cdn.bspapp.com/cloudstorage/ae40402f-aa53-4344-b553-2322799bebd6.png'
|
|
|
|
+ },
|
|
|
|
+ // 删除按钮图片
|
|
|
|
+ deleteImg: {
|
|
|
|
+ type: String,
|
|
|
|
+ default: 'https://mp-61599c79-d7ee-4a75-a24b-e5a288da6dd3.cdn.bspapp.com/cloudstorage/d20177a5-417e-4c5d-a266-1988361c543d.png'
|
|
|
|
+ },
|
|
|
|
+ // 关闭视频按钮图片
|
|
|
|
+ closeImg: {
|
|
|
|
+ type: String,
|
|
|
|
+ default: 'https://mp-61599c79-d7ee-4a75-a24b-e5a288da6dd3.cdn.bspapp.com/cloudstorage/cde4362d-7ec7-4cac-a692-12e1f576be1e.png'
|
|
|
|
+ },
|
|
|
|
+ },
|
|
|
|
+ data() {
|
|
|
|
+ return {
|
|
|
|
+ add:true,
|
|
|
|
+ // 文件类型 'image', 'video', 'all'
|
|
|
|
+ fileType: "all",
|
|
|
|
+ // 渲染列表
|
|
|
|
+ FileList: [],
|
|
|
|
+
|
|
|
|
+ // 预览视频地址
|
|
|
|
+ tempVideoUrl: '',
|
|
|
|
+
|
|
|
|
+ // 临时文件列表
|
|
|
|
+ tempFile_paths: [],
|
|
|
|
+
|
|
|
|
+ };
|
|
|
|
+ },
|
|
|
|
+ watch: {
|
|
|
|
+ // #ifdef VUE2
|
|
|
|
+ 'value': {
|
|
|
|
+ handler: function(newVal, oldVal) {
|
|
|
|
+ this.FileList = newVal;
|
|
|
|
+ },
|
|
|
|
+ deep: true,
|
|
|
|
+ immediate: true
|
|
|
|
+ },
|
|
|
|
+ // #endif
|
|
|
|
+
|
|
|
|
+ // #ifdef VUE3
|
|
|
|
+ 'modelValue': {
|
|
|
|
+ handler: function(newVal, oldVal) {
|
|
|
|
+ this.FileList = newVal;
|
|
|
|
+ },
|
|
|
|
+ deep: true,
|
|
|
|
+ immediate: true
|
|
|
|
+ },
|
|
|
|
+ // #endif
|
|
|
|
+ },
|
|
|
|
+ computed: {
|
|
|
|
+ previewList() {
|
|
|
|
+ return this.FileList.map(item => item.path || item)
|
|
|
|
+ },
|
|
|
|
+ listRowStyle() {
|
|
|
|
+ const style = {
|
|
|
|
+ 'grid-template-columns': `repeat(${this.listStyle?.columns || 4}, 1fr)`, // 每行数量
|
|
|
|
+ 'grid-column-gap': this.listStyle?.columnGap || '40rpx', // 行间距
|
|
|
|
+ 'grid-row-gap': this.listStyle?.rowGap || '40rpx', // 列间距
|
|
|
|
+ 'padding': this.listStyle?.padding || '0rpx' // 列表内边距
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return style;
|
|
|
|
+ },
|
|
|
|
+ rowStyle() {
|
|
|
|
+ const style = {
|
|
|
|
+ 'aspect-ratio': this.listStyle?.height ? '' : this.listStyle?.ratio || '1/1', // 图片比例
|
|
|
|
+ 'height': this.listStyle?.height || '140rpx',
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return style;
|
|
|
|
+ },
|
|
|
|
+ imgStyle() {
|
|
|
|
+ const style = {
|
|
|
|
+ 'border-radius': this.listStyle?.radius || '6rpx', // 图片圆角
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return style;
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+ methods: {
|
|
|
|
+ /**
|
|
|
|
+ * 删除已选择文件
|
|
|
|
+ * @param {object} item 文件信息
|
|
|
|
+ * @param {number} index 文件索引
|
|
|
|
+ * */
|
|
|
|
+ onRemove(item, index) {
|
|
|
|
+ if(this.FileList.length==1){
|
|
|
|
+ this.fileType = "all"
|
|
|
|
+ this.add = true
|
|
|
|
+ }
|
|
|
|
+ const imageItem = this.FileList[index];
|
|
|
|
+
|
|
|
|
+ if (this.useBeforeDelete) {
|
|
|
|
+ this.$emit('beforeDelete', imageItem, index, () => {
|
|
|
|
+ return deleteItem()
|
|
|
|
+ })
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (!this.useBeforeDelete) {
|
|
|
|
+ uni.showModal({
|
|
|
|
+ title: this.deleteTitle,
|
|
|
|
+ content: this.deleteText,
|
|
|
|
+ success: (res) => {
|
|
|
|
+ if (res.confirm) {
|
|
|
|
+ deleteItem()
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ const deleteItem = () => {
|
|
|
|
+ const tempFileIndex = this.tempFile_paths.indexOf(item || item.path);
|
|
|
|
+
|
|
|
|
+ if (tempFileIndex > -1) {
|
|
|
|
+ this.tempFile_paths.splice(tempFileIndex, 1)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ this.FileList.splice(index, 1)
|
|
|
|
+
|
|
|
|
+ // #ifdef VUE2
|
|
|
|
+ this.$emit('input', this.FileList)
|
|
|
|
+ // #endif
|
|
|
|
+
|
|
|
|
+ // #ifdef VUE3
|
|
|
|
+ this.$emit("update:modelValue", this.FileList);
|
|
|
|
+ // #endif
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 点击已选择文件
|
|
|
|
+ * @param {object} item 文件信息
|
|
|
|
+ * @param {number} index 文件索引
|
|
|
|
+ * */
|
|
|
|
+ onClickRow(item, index) {
|
|
|
|
+ this.previewImage(item?.url ?? item, index);
|
|
|
|
+ this.$emit('onImage', {
|
|
|
|
+ item,
|
|
|
|
+ index
|
|
|
|
+ })
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 点击选择图片按钮
|
|
|
|
+ * */
|
|
|
|
+ onClickAdd() {
|
|
|
|
+
|
|
|
|
+ switch (this.fileType) {
|
|
|
|
+ case 'image':
|
|
|
|
+ this.onChooseFile(1);
|
|
|
|
+ break;
|
|
|
|
+ case 'video':
|
|
|
|
+ this.onChooseFile(2);
|
|
|
|
+ break;
|
|
|
|
+ case 'all':
|
|
|
|
+ uni.showActionSheet({
|
|
|
|
+ itemList: ['照片', '视频'],
|
|
|
|
+ success: (res) => {
|
|
|
|
+ const tapIndex = res.tapIndex;
|
|
|
|
+ if (tapIndex === 0) {
|
|
|
|
+ this.onChooseFile(1);
|
|
|
|
+ } else {
|
|
|
|
+ this.onChooseFile(2);
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+ fail: (res) => {
|
|
|
|
+ console.error(res.errMsg);
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+ break;
|
|
|
|
+ default:
|
|
|
|
+ this.onChooseFile(1);
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 从本地选择文件。
|
|
|
|
+ * @param { number } updataType 选择类型 1:图片 2视频
|
|
|
|
+ * */
|
|
|
|
+ async onChooseFile(updataType) {
|
|
|
|
+ const that = this;
|
|
|
|
+ if (updataType === 1) {
|
|
|
|
+ this.fileType = 'image'
|
|
|
|
+ this.$emit('showType', "1")
|
|
|
|
+ const data = Object.assign({}, {
|
|
|
|
+ // 最多可以选择的图片张数,默认9
|
|
|
|
+ count: 9,
|
|
|
|
+ // 仅对 mediaType 为 image 时有效,是否压缩所选文件
|
|
|
|
+ // #ifndef MP-TOUTIAO
|
|
|
|
+ sizeType: ['original', 'compressed'],
|
|
|
|
+ // #endif
|
|
|
|
+ // album 从相册选图,camera 使用相机,默认二者都有。
|
|
|
|
+ sourceType: ['camera', 'album'],
|
|
|
|
+
|
|
|
|
+ compress: false
|
|
|
|
+ }, this.imageFormData)
|
|
|
|
+
|
|
|
|
+ data['count'] = this.max - this.FileList.length
|
|
|
|
+
|
|
|
|
+ uni.chooseImage({
|
|
|
|
+ ...data,
|
|
|
|
+ success: async (res) => {
|
|
|
|
+ let tempFiles = res.tempFiles
|
|
|
|
+ const compress = that.imageFormData?.compress || false;
|
|
|
|
+
|
|
|
|
+ // 限制图片上传尺寸
|
|
|
|
+ if (that.imageFormData?.size ?? false) {
|
|
|
|
+ tempFiles.map((imgInfo, index) => {
|
|
|
|
+ const maxSize = that.imageFormData.size * 1024 * 1024
|
|
|
|
+ if (imgInfo.size > maxSize) {
|
|
|
|
+ tempFiles.splice(index, 1)
|
|
|
|
+ that.$emit('onImageSize', imgInfo)
|
|
|
|
+ return uni.showToast({
|
|
|
|
+ title: `图片最大上传${that.imageFormData.size}MB`,
|
|
|
|
+ duration: 2000,
|
|
|
|
+ icon: 'none'
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
+ })
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 开启压缩图片
|
|
|
|
+ if (compress) {
|
|
|
|
+ const compressImage = tempFiles.map(imageItem => {
|
|
|
|
+ return that.compressImage(imageItem.path)
|
|
|
|
+ })
|
|
|
|
+
|
|
|
|
+ Promise.all(compressImage).then(result => {
|
|
|
|
+ upload(result);
|
|
|
|
+ })
|
|
|
|
+
|
|
|
|
+ } else {
|
|
|
|
+ upload(tempFiles);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ function upload(tempImages) {
|
|
|
|
+ if (that.autoUpload) {
|
|
|
|
+ tempImages.map(item => {
|
|
|
|
+ that.onBeforeUploadFile(item, 'image')
|
|
|
|
+ })
|
|
|
|
+ } else {
|
|
|
|
+ that.FileList = [...that.FileList, ...tempImages]
|
|
|
|
+ tempImages.map(item => {
|
|
|
|
+ that.tempFile_paths.push(item)
|
|
|
|
+ })
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ },
|
|
|
|
+ fail(err) {
|
|
|
|
+ console.error('选择图片失败', err)
|
|
|
|
+ that.$emit('onError', err)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ })
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (updataType === 2) {
|
|
|
|
+ console.log("上传视频")
|
|
|
|
+ this.fileType = "video"
|
|
|
|
+ this.$emit('showType', "2")
|
|
|
|
+ this.add = false
|
|
|
|
+ // 限制视频最大上传数量
|
|
|
|
+ const VIDEO_REGEXP = /\.(mp4|flv|avi)/i
|
|
|
|
+ const videoList = await that.FileList.filter(item => {
|
|
|
|
+ const fileUrl = item?.url ?? item
|
|
|
|
+ return VIDEO_REGEXP.test(fileUrl)
|
|
|
|
+ })
|
|
|
|
+
|
|
|
|
+ if (that.maxVideo > 0 && videoList.length >= that.maxVideo) {
|
|
|
|
+ that.$emit('onVideoMax', that.maxVideo, videoList.length)
|
|
|
|
+ return uni.showToast({
|
|
|
|
+ title: '视频数量已超出',
|
|
|
|
+ duration: 2000,
|
|
|
|
+ icon: 'none'
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ const data = Object.assign({}, {
|
|
|
|
+ // 拍摄视频最长拍摄时间,单位秒。最长支持 60 秒。
|
|
|
|
+ maxDuration: 60,
|
|
|
|
+ // #ifndef MP-TOUTIAO
|
|
|
|
+ // 'front'、'back',默认'back'
|
|
|
|
+ camera: "back",
|
|
|
|
+ // #endif
|
|
|
|
+
|
|
|
|
+ // album 从相册选视频,camera 使用相机拍摄,默认二者都有。
|
|
|
|
+ sourceType: ['camera', 'album'],
|
|
|
|
+ // 是否压缩所选的视频源文件,默认值为 true,需要压缩。
|
|
|
|
+ compressed: true,
|
|
|
|
+ // 'front'、'back',默认'back'
|
|
|
|
+ }, this.videoFromData)
|
|
|
|
+
|
|
|
|
+ uni.chooseVideo({
|
|
|
|
+ ...data,
|
|
|
|
+ success: (res) => {
|
|
|
|
+ let tempFilePath = {
|
|
|
|
+ ...res
|
|
|
|
+ }
|
|
|
|
+ tempFilePath['path'] = res.tempFilePath
|
|
|
|
+
|
|
|
|
+ // 限制视频上传尺寸
|
|
|
|
+ if (that.videoFromData?.size ?? false) {
|
|
|
|
+ const maxSize = that.videoFromData.size * 1024 * 1024
|
|
|
|
+
|
|
|
|
+ if (tempFilePath.size > maxSize) {
|
|
|
|
+ uni.showToast({
|
|
|
|
+ title: `视频最大上传${that.videoFromData.size}MB`,
|
|
|
|
+ duration: 2000,
|
|
|
|
+ icon: 'none'
|
|
|
|
+ });
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+ if (that.autoUpload) {
|
|
|
|
+ that.onBeforeUploadFile(tempFilePath, 'video')
|
|
|
|
+ } else {
|
|
|
|
+ that.FileList.push(tempFilePath)
|
|
|
|
+ that.tempFile_paths.push(tempFilePath)
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+ fail(err) {
|
|
|
|
+ console.error('选择视频失败', err)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ })
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ onBeforeUploadFile(tempFile) {
|
|
|
|
+ if (this.useBeforeUpload) {
|
|
|
|
+ return this.$emit('beforeUpload', tempFile, () => {
|
|
|
|
+ return this.updataFile(tempFile);
|
|
|
|
+ })
|
|
|
|
+ }
|
|
|
|
+ return this.updataFile(tempFile);
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 上传文件到服务器
|
|
|
|
+ * @param { tempFile } 临时文件
|
|
|
|
+ * */
|
|
|
|
+ updataFile(tempFile) {
|
|
|
|
+ const that = this;
|
|
|
|
+ const filePath = tempFile.path || tempFile;
|
|
|
|
+ const fileType = this.fileUrlType(filePath) == 'image' ? '.png' : '.mp4';
|
|
|
|
+ const fileName = tempFile.name || Date.now() + fileType;
|
|
|
|
+
|
|
|
|
+ uni.showLoading({
|
|
|
|
+ title: '正在上传中...',
|
|
|
|
+ icon: 'loading'
|
|
|
|
+ })
|
|
|
|
+
|
|
|
|
+ return new Promise((resolve, reject) => {
|
|
|
|
+ // uniCloud上传
|
|
|
|
+ if (that.action === 'uniCloud') {
|
|
|
|
+
|
|
|
|
+ uniCloud.uploadFile({
|
|
|
|
+ cloudPath: String(fileName),
|
|
|
|
+ filePath: filePath,
|
|
|
|
+ // #ifdef MP-ALIPAY
|
|
|
|
+ fileType: fileType,
|
|
|
|
+ // #endif
|
|
|
|
+ onUploadProgress: (progressEvent) => {
|
|
|
|
+ const percentCompleted = Math.round(
|
|
|
|
+ (progressEvent.loaded * 100) / progressEvent.total
|
|
|
|
+ );
|
|
|
|
+ that.$emit('onProgress', percentCompleted)
|
|
|
|
+ },
|
|
|
|
+ success(result) {
|
|
|
|
+ if (that.autoUpload) {
|
|
|
|
+ that.FileList.push(result.fileID)
|
|
|
|
+ } else {
|
|
|
|
+ that.FileList.map((item, index) => {
|
|
|
|
+ if (item === filePath || item.path === filePath) {
|
|
|
|
+ that.FileList.splice(index, 1, result.fileID)
|
|
|
|
+ }
|
|
|
|
+ })
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // #ifdef VUE2
|
|
|
|
+ that.$emit('input', that.FileList)
|
|
|
|
+ // #endif
|
|
|
|
+ // #ifdef VUE3
|
|
|
|
+ that.$emit("update:modelValue", that.FileList);
|
|
|
|
+ // #endif
|
|
|
|
+
|
|
|
|
+ resolve(result.fileID)
|
|
|
|
+ uni.hideLoading();
|
|
|
|
+ that.$emit('onProgress', {
|
|
|
|
+ ...result
|
|
|
|
+ })
|
|
|
|
+ },
|
|
|
|
+ fail: (error) => {
|
|
|
|
+ uni.hideLoading();
|
|
|
|
+ console.error('error', error);
|
|
|
|
+ that.$emit('onError', error)
|
|
|
|
+ reject(error)
|
|
|
|
+ }
|
|
|
|
+ })
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+ const uploadTask = uploadImage(filePath, 'cardImages/',
|
|
|
|
+ result => {
|
|
|
|
+ const data = result
|
|
|
|
+ uni.hideLoading();
|
|
|
|
+ that.success(data)
|
|
|
|
+
|
|
|
|
+ if (!this.autoUpload) {
|
|
|
|
+ that.FileList.map((item, index) => {
|
|
|
|
+ if (item === filePath || item.path === filePath) {
|
|
|
|
+ that.FileList.splice(index, 1)
|
|
|
|
+ }
|
|
|
|
+ })
|
|
|
|
+ }
|
|
|
|
+ resolve(data)
|
|
|
|
+ }, "", that.fileUrlType(filePath)
|
|
|
|
+ );
|
|
|
|
+ // 接口服务上传
|
|
|
|
+ // const uploadTask = uni.uploadFile({
|
|
|
|
+ // url: that.action,
|
|
|
|
+ // filePath: filePath,
|
|
|
|
+ // name: that.fileName,
|
|
|
|
+ // formData: that.data,
|
|
|
|
+ // header: that.headers,
|
|
|
|
+ // success: (uploadFileRes) => {
|
|
|
|
+ // const data = JSON.parse(uploadFileRes.data)
|
|
|
|
+ // uni.hideLoading();
|
|
|
|
+ // that.success(data)
|
|
|
|
+
|
|
|
|
+ // if (!this.autoUpload) {
|
|
|
|
+ // that.FileList.map((item, index) => {
|
|
|
|
+ // if (item === filePath || item.path === filePath) {
|
|
|
|
+ // that.FileList.splice(index, 1)
|
|
|
|
+ // }
|
|
|
|
+ // })
|
|
|
|
+ // }
|
|
|
|
+
|
|
|
|
+ // resolve(data)
|
|
|
|
+ // },
|
|
|
|
+ // fail: (error) => {
|
|
|
|
+ // uni.hideLoading();
|
|
|
|
+ // console.error('error', error);
|
|
|
|
+ // that.$emit('onError', error)
|
|
|
|
+ // reject(error)
|
|
|
|
+ // }
|
|
|
|
+ // });
|
|
|
|
+
|
|
|
|
+ // uploadTask.onProgressUpdate((res) => {
|
|
|
|
+ // that.$emit('onProgress', {
|
|
|
|
+ // ...res,
|
|
|
|
+ // ...tempFile
|
|
|
|
+ // })
|
|
|
|
+ // });
|
|
|
|
+ })
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 手动上传
|
|
|
|
+ * */
|
|
|
|
+ submit() {
|
|
|
|
+
|
|
|
|
+ return new Promise((resolve, reject) => {
|
|
|
|
+ if (this.tempFile_paths.length <= 0) {
|
|
|
|
+ return console.error('没有可上传文件');
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ const result = this.tempFile_paths.map(item => {
|
|
|
|
+ return this.onBeforeUploadFile(item || item.path)
|
|
|
|
+ })
|
|
|
|
+
|
|
|
|
+ Promise.all(result).then(res => {
|
|
|
|
+ this.tempFile_paths = []
|
|
|
|
+ resolve(res)
|
|
|
|
+ }).catch(err => {
|
|
|
|
+ reject(err)
|
|
|
|
+ })
|
|
|
|
+ })
|
|
|
|
+
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 返回数据
|
|
|
|
+ * */
|
|
|
|
+ success(data) {
|
|
|
|
+ this.$emit('onSuccess', data);
|
|
|
|
+
|
|
|
|
+ // 自定义数据结构-选择性开启
|
|
|
|
+ // const list = data.map(item=> {
|
|
|
|
+ // return JSON.parse(item).data.link;
|
|
|
|
+ // })
|
|
|
|
+ // this.$emit('input', [...this.FileList, ...list]);
|
|
|
|
+ },
|
|
|
|
+ /**
|
|
|
|
+ * 压缩图片
|
|
|
|
+ * @param {array} tempFilePaths 临时路径数组
|
|
|
|
+ * @return {array} 被压缩过的路径数组
|
|
|
|
+ * */
|
|
|
|
+ async compressImage(tempFilePaths) {
|
|
|
|
+ const that = this;
|
|
|
|
+
|
|
|
|
+ return new Promise((resolve, reject) => {
|
|
|
|
+
|
|
|
|
+ if (typeof tempFilePaths !== 'string') {
|
|
|
|
+ console.error('压缩路径错误')
|
|
|
|
+ reject([])
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ uni.showLoading({
|
|
|
|
+ title: '压缩中...',
|
|
|
|
+ icon: 'loading',
|
|
|
|
+ })
|
|
|
|
+
|
|
|
|
+ // #ifdef H5
|
|
|
|
+ this.canvasDataURL(tempFilePaths, {
|
|
|
|
+ quality: that.imageFormData.quality / 100
|
|
|
|
+ }, (base64Codes) => {
|
|
|
|
+ resolve(base64Codes);
|
|
|
|
+ uni.hideLoading();
|
|
|
|
+ })
|
|
|
|
+ // #endif
|
|
|
|
+
|
|
|
|
+ // #ifndef H5
|
|
|
|
+ uni.compressImage({
|
|
|
|
+ src: tempFilePaths,
|
|
|
|
+ quality: that.imageFormData.quality || 80,
|
|
|
|
+ success: res => {
|
|
|
|
+ resolve(res.tempFilePath);
|
|
|
|
+ uni.hideLoading();
|
|
|
|
+ },
|
|
|
|
+ fail(err) {
|
|
|
|
+ reject(err);
|
|
|
|
+ uni.hideLoading();
|
|
|
|
+ }
|
|
|
|
+ })
|
|
|
|
+ // #endif
|
|
|
|
+
|
|
|
|
+ })
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * H5压缩图片质量
|
|
|
|
+ * */
|
|
|
|
+ canvasDataURL(path, obj, callback) {
|
|
|
|
+ var img = new Image();
|
|
|
|
+ img.src = path;
|
|
|
|
+ img.onload = function() {
|
|
|
|
+ var that = this;
|
|
|
|
+ // 默认按比例压缩
|
|
|
|
+ var w = that.width,
|
|
|
|
+ h = that.height,
|
|
|
|
+ scale = w / h;
|
|
|
|
+ w = obj.width || w;
|
|
|
|
+ h = obj.height || (w / scale);
|
|
|
|
+ var quality = 0.8; // 默认图片质量为0.8
|
|
|
|
+ //生成canvas
|
|
|
|
+ var canvas = document.createElement('canvas');
|
|
|
|
+ var ctx = canvas.getContext('2d');
|
|
|
|
+ // 创建属性节点
|
|
|
|
+ var anw = document.createAttribute("width");
|
|
|
|
+ anw.nodeValue = w;
|
|
|
|
+ var anh = document.createAttribute("height");
|
|
|
|
+ anh.nodeValue = h;
|
|
|
|
+ canvas.setAttributeNode(anw);
|
|
|
|
+ canvas.setAttributeNode(anh);
|
|
|
|
+ ctx.drawImage(that, 0, 0, w, h);
|
|
|
|
+ // 图像质量
|
|
|
|
+ if (obj.quality && obj.quality <= 1 && obj.quality > 0) {
|
|
|
|
+ quality = obj.quality;
|
|
|
|
+ }
|
|
|
|
+ // quality值越小,所绘制出的图像越模糊
|
|
|
|
+ var base64 = canvas.toDataURL('image/jpeg', quality);
|
|
|
|
+ // 回调函数返回base64的值
|
|
|
|
+ callback(base64);
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 预览图片
|
|
|
|
+ * @param {string, object} item 文件信息
|
|
|
|
+ * */
|
|
|
|
+ previewImage(item) {
|
|
|
|
+ if (this.fileUrlType(item) === 'video') return false;
|
|
|
|
+ if (!this.isPreviewImage) return false;
|
|
|
|
+
|
|
|
|
+ const imgs = this.FileList.filter(item => {
|
|
|
|
+ return this.fileUrlType(item) !== 'video'
|
|
|
|
+ }).map(item => item?.path ?? item)
|
|
|
|
+ const current = imgs.indexOf(item || item.path);
|
|
|
|
+
|
|
|
|
+ uni.previewImage({
|
|
|
|
+ current: current,
|
|
|
|
+ urls: imgs,
|
|
|
|
+ success() {},
|
|
|
|
+ fail(err) {
|
|
|
|
+ console.log(err);
|
|
|
|
+ }
|
|
|
|
+ })
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 预览视频
|
|
|
|
+ * @param {string, object} item 文件信息
|
|
|
|
+ * */
|
|
|
|
+ onPlay(item, index) {
|
|
|
|
+ this.$emit('onVideo', {
|
|
|
|
+ item,
|
|
|
|
+ index
|
|
|
|
+ })
|
|
|
|
+ this.tempVideoUrl = item.url || item;
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 是否img类型
|
|
|
|
+ * @param {string, object} item 文件信息
|
|
|
|
+ * */
|
|
|
|
+ fileUrlType(file) {
|
|
|
|
+ const filePath = file.path || file;
|
|
|
|
+
|
|
|
|
+ if (this.isBase64(filePath)) return 'image'
|
|
|
|
+
|
|
|
|
+ const fileType = filePath.split('.').pop();
|
|
|
|
+
|
|
|
|
+ const IMAGE_REGEXP = /(jpeg|jpg|gif|png|svg|webp|jfif|bmp|dpg|image)/i
|
|
|
|
+ if (IMAGE_REGEXP.test(fileType)) {
|
|
|
|
+ return 'image';
|
|
|
|
+ } else {
|
|
|
|
+ return 'video';
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+ isBase64(str) {
|
|
|
|
+ if (str === '' || typeof str !== 'string') return console.error('文件路径错误, base64', str);
|
|
|
|
+ return str.includes('blob:') || str.includes('data:image');
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+</script>
|
|
|
|
+
|
|
|
|
+<style lang="scss" scoped>
|
|
|
|
+ .cl-updata {
|
|
|
|
+
|
|
|
|
+ .file-list {
|
|
|
|
+ display: grid;
|
|
|
|
+
|
|
|
|
+ &-row {
|
|
|
|
+ display: inline-flex;
|
|
|
|
+ align-items: center;
|
|
|
|
+ position: relative;
|
|
|
|
+
|
|
|
|
+ image {
|
|
|
|
+ height: 100%;
|
|
|
|
+ width: 100%;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ ._video {
|
|
|
|
+ position: relative;
|
|
|
|
+ width: 100%;
|
|
|
|
+ height: 100%;
|
|
|
|
+ overflow: hidden;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ .video-fixed {
|
|
|
|
+ position: absolute;
|
|
|
|
+ top: 0;
|
|
|
|
+ left: 0;
|
|
|
|
+ bottom: 0;
|
|
|
|
+ width: 100%;
|
|
|
|
+ height: 100%;
|
|
|
|
+ border-radius: 10rpx;
|
|
|
|
+ z-index: 96;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ .play {
|
|
|
|
+ position: absolute;
|
|
|
|
+ top: 50%;
|
|
|
|
+ left: 50%;
|
|
|
|
+ transform: translate(-50%, -50%);
|
|
|
|
+ width: 30%;
|
|
|
|
+ z-index: 95;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ .app_play {
|
|
|
|
+ position: absolute;
|
|
|
|
+ top: 50%;
|
|
|
|
+ left: 50%;
|
|
|
|
+ transform: translate(-50%, -50%);
|
|
|
|
+ width: 50rpx;
|
|
|
|
+ height: 50rpx;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ .remove {
|
|
|
|
+ position: absolute;
|
|
|
|
+ top: 0;
|
|
|
|
+ right: 0;
|
|
|
|
+ background-color: #373737;
|
|
|
|
+ height: 50rpx;
|
|
|
|
+ width: 50rpx;
|
|
|
|
+ border-bottom-left-radius: 200rpx;
|
|
|
|
+ z-index: 97;
|
|
|
|
+
|
|
|
|
+ .image {
|
|
|
|
+ width: 20rpx;
|
|
|
|
+ height: 20rpx;
|
|
|
|
+ position: absolute;
|
|
|
|
+ right: 12rpx;
|
|
|
|
+ top: 12rpx;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ .add-image {
|
|
|
|
+ display: flex;
|
|
|
|
+ align-items: center;
|
|
|
|
+ justify-content: center;
|
|
|
|
+ border: 2rpx dashed #ccc;
|
|
|
|
+
|
|
|
|
+ &:active {
|
|
|
|
+ opacity: 0.8;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ image {
|
|
|
|
+ width: 40%;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ .mask {
|
|
|
|
+ background-color: #000;
|
|
|
|
+ position: fixed;
|
|
|
|
+ top: 0;
|
|
|
|
+ right: 0;
|
|
|
|
+ bottom: 0;
|
|
|
|
+ left: 0;
|
|
|
|
+ z-index: 99;
|
|
|
|
+
|
|
|
|
+ .block {
|
|
|
|
+ padding: 0 30rpx;
|
|
|
|
+ position: absolute;
|
|
|
|
+ top: 50%;
|
|
|
|
+ left: 50%;
|
|
|
|
+ transform: translate(-50%, -50%);
|
|
|
|
+ width: 100%;
|
|
|
|
+
|
|
|
|
+ video {
|
|
|
|
+ width: 100%;
|
|
|
|
+ height: 78vh;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ ._root {
|
|
|
|
+ width: 60rpx;
|
|
|
|
+ height: 60rpx;
|
|
|
|
+ position: absolute;
|
|
|
|
+ left: 40rpx;
|
|
|
|
+ top: 5vh
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+</style>
|