uniapp文件上传

<template>

    <view class="cl-updata">

        <view class="file-list" :style="[listRowStyle]">

            <view v-for="(item, index) in modelValue" @tap.stop="clickSelectedFile(item, index)" class="file-list-item">

                <image

                    class="_image"

                    v-if="item.fileType === 'image'"

                    :src="setFileUrl(item.watermarkUrl) || setFileUrl(item.url)"

                    :style="{borderRadius:'12rpx',overflow:'hidden'}"

                    mode="aspectFill">

                </image>

                <div class="_image" style="background-color: #d3e3fd;" v-if="item.fileType === 'video'">

                    <image

                        class="_video"

                        :src="setFileUrl(item.poster) || setFileUrl(item.url)"

                        :style="{borderRadius:'12rpx',overflow:'hidden'}"

                        :fade-show="false"

                        mode="aspectFill">

                    </image>

                    <!-- <image :src="playImg" mode="aspectFill" ></image> -->

                    <!-- <div  class="play"></div> -->

                    <u-icon name="play-right-fill" class="play" color="#fff" size="30" ></u-icon>

                </div>

                <image

                    class="_image"

                    v-if="item.fileType !== 'image' && item.fileType !== 'video' "

                    :src="setFileUrl(item.watermarkUrl) || setFileUrl(item.url)"

                    :style="{borderRadius:'12rpx',overflow:'hidden'}"

                    mode="aspectFill">

                </image>

                <view class="remove" v-if="remove" @tap.stop="deleteSelectedFile(item, index)">

                    <u-icon name="close" class="image" size="16" color="#fff" ></u-icon>

                </view>

            </view>

            <view v-if="add && FileList.length < max" @tap.stop="selectFileTypeOnAdd" class="file-list-item">

                <slot name="addImg">

                    <div class="add-image">

                        <image style="width: 100%;height: 100%;" src="@/static/img/xiangji.png" mode="widthFix"></image>

                    </div>

                </slot>

            </view>

        </view>

        <Teleport to="body">

            <div v-if="tempVideoUrl"

            style="

                background-color: #000 !important;

                position: fixed !important;

                top: 0 !important;

                right: 0 !important;

                bottom: 0 !important;

                left: 0 !important;

                z-index: 99999 !important;

                display: flex;

                flex-direction: column;

            "

            >

                <u-icon @click="tempVideoUrl = ''"  :blod="true" name="close" style="margin-left: 30rpx;margin-top: 50rpx;" size="24" color="#fff" ></u-icon>

                <div style="box-sizing: border-box;padding: 30rpx;width: 100%;flex: 1;overflow: hidden;background-color: transparent;" @tap.stop>

                    <video style="width: 100%;height: 100%;background-color: transparent;" autoplay :src="setFileUrl(tempVideoUrl)"></video>

                </div>

            </div>

        </Teleport>

    </view>

</template>

<script>

export default {

    name: "cl-upload",

    components: {  },

    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: () => {

                return {}

            }

        },

        // 上传视频参数

        videoFromData: {

            type: Object,

            default: () => { }

        },

        // 必选参数,上传的地址

        action: {

            type: String,

            default: ''

        },

       

        // 启用目录, 仅unicloud阿里云支持

        // https://uniapp.dcloud.net.cn/uniCloud/storage.html#storage-dir

        cloudPathAsRealPath: {

            type: Boolean,

            default: false

        },

        // 设置上传的请求头部

        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: 5

        },

        // 视频最大上传数量

        maxVideo: {

            type: Number,

            default: 0

        },

        // 列表样式

        listStyle: {

            type: Object,

            default: () => { }

        },

        // 删除提示弹窗标题

        deleteTitle: {

            type: String,

            default: '提示'

        },

        // 删除提示弹窗文案

        deleteText: {

            type: String,

            default: '您确认要删除吗?'

        },

        // 加载文案

        loadingText: {

            type: String,

            default: '正在上传中...'

        },

        // 是否开启删除前钩子

        useBeforeDelete: {

            type: Boolean,

            default: false

        },

        // 是否开启上传前钩子

        useBeforeUpload: {

            type: Boolean,

            default: false

        },

    },

    data() {

        return {

            // 渲染列表

            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: {

        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;

        },

        imgStyle() {

            const style = {

                'border-radius': '6rpx', // 图片圆角

            }

            return style;

        }

    },

    methods: {

        /**

         * 删除已选择文件

         * @param {object} item 文件信息

         * @param {number} selectedFileIndex 文件索引

         * */

        deleteSelectedFile(item, selectedFileIndex) {

            this.$emit('beofreDelete',selectedFileIndex,item.fileType)

        },

        /**

         * 点击已选择文件

         * @param {object} item 文件信息

         * @param {number} index 文件索引

         * */

        clickSelectedFile(item, index) {

            if(this.previewImage(item, index) === 'video' ){

                this.previewVideo(item,index)

            }

        },

        /**

         * 点击选择图片按钮

         * */

        selectFileTypeOnAdd() {

            switch (this.fileType) {

                case 'image':

                    this.handleFileSelection(1);

                    break;

                case 'video':

                    this.handleFileSelection(2);

                    break;

                case 'all':

                    uni.showActionSheet({

                        itemList: ['图片', '视频'],

                        success: (res) => {

                            const tapIndex = res.tapIndex || res;

                            console.log(res,'选择');

                            console.log(typeof res.tapIndex,'选择框类型');

                           

                            if (tapIndex == 1) {

                                this.handleFileSelection(2);

                            } else {

                                this.handleFileSelection(1);

                            }

                        },

                        fail: (res) => {

                            console.error(res.errMsg);

                        }

                    });

                    break;

                default:

                    this.handleFileSelection(1);

                    break;

            }

        },


 

        /**

         * 从本地选择文件。

         * @param { number } updataType 选择类型 1:图片 2视频

         * */

        async handleFileSelection(updataType) {

            const that = this;

            if (updataType === 1) {

                const data = Object.assign({}, {

                    // 最多可以选择的图片张数,默认9

                    count: 1,

                    // 仅对 mediaType 为 image 时有效,是否压缩所选文件

                    // album 从相册选图,camera 使用相机,默认二者都有。

                    sourceType: ['camera', 'album'],

                    compress: false

                }, this.imageFormData)

                data['count'] = this.max - this.FileList.length

                uni.chooseImage({

                    ...data,count:1,compress: false,

                    success: async (res) => {

                        res.tempFile = res.tempFiles[0];

                        res.tempFile.fileType = 'image'

                        this.$emit('change',res)

                    },

                    fail(err) {

                        console.error('选择图片失败', err)

                        that.$emit('onError', err)

                    }

                })

            }

            if (updataType === 2) {

                const data = Object.assign({}, {

                    //  拍摄视频最长拍摄时间,单位秒。最长支持 60 秒。

                    maxDuration: 60,

                    // album 从相册选视频,camera 使用相机拍摄,默认二者都有。

                    sourceType: ['camera', 'album'],

                    // 是否压缩所选的视频源文件,默认值为 true,需要压缩。

                    compressed: false,

                    count: 1,

                    // 'front'、'back',默认'back'

                }, this.videoFromData)

                uni.chooseVideo({

                    ...data,count:1,compressed: false,

                    success: (res) => {

                        let tempFilePath = { ...res }

                        tempFilePath['path'] = res.tempFilePath

                        tempFilePath.type = tempFilePath.type || tempFilePath?.tempFile?.type

                        res.tempFile.fileType = 'video'

                        this.$emit('change',res)

                    },

                    fail(err) {

                        console.error('选择视频失败', err)

                    }

                })

            }

        },

        /**

         * 压缩图片

         * @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压缩图片质量

         * @param {string} path 图片路径

         * @param {object} obj 压缩配置

         * @param {function} callback 回调函数

         * @return {string} base64

         * */

        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 (item.fileType === 'video') return 'video';

            if (!this.isPreviewImage) return false;

            const imgs = this.FileList.filter(item => {

                return item.fileType !== 'video'

            }).map(item => ( this.setFileUrl(item.watermarkUrl) || this.setFileUrl(item.url) ) )

            const current = imgs.indexOf(this.setFileUrl(item.watermarkUrl) || this.setFileUrl(item.url) );

           

            uni.previewImage({

                current: current,

                urls: imgs,

                success() {

                },

                fail(err) {

                    console.log(err);

                }

            })

        },

        /**

         * 预览视频

         * @param {string, object} item 文件信息

         * @param {number} index 索引

         * */

        previewVideo(item, index) {

            this.$emit('onVideo', {

                item,

                index

            })

            this.tempVideoUrl = this.setFileUrl(item.watermarkUrl) || this.setFileUrl(item.url);

        },

    }

}

</script>

<style lang="scss" scoped>

.cl-updata {

    .file-list {

        display: flex;

        flex-wrap: wrap;

        .file-list-item{

            width: 100px;

            height: 100px;

            position: relative;

            .play-img {

                width: 100%;

            }

            ._image {

                height: 100%;

                width: 100%;

                border-radius: 12rpx;

            }

            ._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%);

                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: 30px;

                width: 30px;

                display: flex;

                align-items: center;

                justify-content: center;

                z-index: 97;

                .image {

                    // width: 20rpx;

                    // height: 20rpx;

                }

            }

            .add-image {

                display: flex;

                align-items: center;

                justify-content: center;

                // border: 2rpx dashed #ccc;

                width: 100%;

                height: 100%;

                border-radius: 5rpx;

                &:active {

                    opacity: 0.8;

                }

                ._image {

                    width: 40%;

                }

            }

        }

    }

}

</style>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值