基于Element upload的文件上传组件

前言

平常项目中经常会用到文件上传功能,由于每次都写比较麻烦,就简单进行了封装,方便重复使用。写了两个版本,根据需要选择,因为没有测试(缺失上传地址),可能存在一些bug。

文件列表版

<template>
    <div v-loading="loading" element-loading-text="拼命上传中"
         element-loading-spinner="el-icon-loading"
         element-loading-background="rgba(0, 0, 0, 0.8)">
        <el-row>
            <el-col :span="4">
                <el-form-item label="附件文档">
                    <el-upload ref="file" :data="params" :action="$uploadUrl" name="file" multiple :show-file-list="false" :limit="maxNumber" :file-list="fileList"
                               :before-upload="checkFiles" :on-exceed="handleExceed" :on-success="uploadSuccess" :on-error="uploadError">
                        <el-button size="small">选择文件</el-button>
                    </el-upload>
                </el-form-item>
            </el-col>
            <el-col :span="20">
                <!-- 使用插槽进行自定义 -->
                <slot name="uploadTip"></slot>
                <slot name="default">
                    <el-form-item label="附件">
                        <el-input :value="'支持PDF、JPEG、PNG、JPG、word、Excel、PPT等格式'" disabled></el-input>
                    </el-form-item>
                </slot>
            </el-col>
        </el-row>
        <el-row>
            <el-col :span="24">
                <el-form-item label="附件列表"></el-form-item>
                <div class="file-list">
                    <el-tag
                        v-for="(tag,index) in fileList"
                        :key="tag.fileCode"
                        class="file-tag"
                        :closable="closable"
                        :type="tag.type"
                        @click="handleClick(tag)"
                        @close="handleRemove(tag,index)">
                        {
    
    {
    
     tag.sourceName }}
                    </el-tag>
                </div>
            </el-col>
        </el-row>
        <!-- 图片预览 -->
        <el-dialog title="预览" :visible="openView" width="460px" :close-on-click-modal="false" append-to-body @close="openView=false">
            <el-image class="img-view" :src="fileSrc" fit="fill"></el-image>
        </el-dialog>
    </div>
</template>

<script>
export default {
    
    
    props: {
    
    
        // 允许上传的文件裂隙
        fileType: {
    
    
            type: Array,
            default: () => ['pdf', 'jpg', 'jpeg', 'png', 'xls', 'xlsx', 'doc', 'docx','ppt']
        },
        // 最大文件上传数
        maxNumber: {
    
    
            type: Number,
            default: 4
        },
        // 上传时额外参数
        params: {
    
    
            type: Object,
            default: () => {
    
    }
        },
        // 用于回显
        fileList: {
    
    
            type: Array,
            default: () => []
        },
        closable: {
    
    
            type: Boolean,
            default: true
        }
    },
    data() {
    
    
        return {
    
    
            fileSrc: '',
            openView: false,
            // 上传加载
            loading: false
        };
    },
    methods: {
    
    
        // 获取文件后缀
        getFileSuffix(file,name) {
    
    
            let fileName = file[name].toLocaleLowerCase();
            return fileName.substr(fileName.lastIndexOf('.') + 1);
        },
        // 确定文件格式
        checkFiles(file) {
    
    
            // 文件后缀
            let suffix = this.getFileSuffix(file,'name');
            if(!this.fileType.includes(suffix)) {
    
    
                this.$message('格式错误,请按提示选择正确的文件格式');
                // before-upload 必须返回false,否则还会继续上传
                return false;
            }
            this.loading = true;
        },
        // 文件超限提醒
        handleExceed(files, fileList) {
    
    
            this.$message.warning(`当前限制选择 ${
      
      this.maxNumber} 个文件,本次选择了 ${
      
      files.length} 个文件,共选择了 ${
      
      files.length + fileList.length} 个文件`);
        },
        // 文件移除
        handleRemove(file, index) {
    
    
            this.$confirm('确定删除?', '提示', {
    
    
                confirmButtonText: '确定',
                showCancelButton: '取消',
                type: 'warning'
            }).then(() => {
    
    
                this.fileList.splice(index, 1);
                // 返回文件信息
                this.$emit('getFileInfo',this.fileList);
            }).catch(() => {
    
    
                this.$message({
    
     type: 'info', message: '已取消删除' });
            });
        },
        // 上传成功
        uploadSuccess(response, file, fileList) {
    
    
            this.loading = false;
            // 这里存在一个问题,当第二次上传时,之前上传的文件也会存在fileList中,但是之前的文件信息不存在response属性
            // 解决方式:跳过之前的文件信息判断
            let size = this.fileList.length; // 获取之前的文件个数
            if(fileList.every(e => e.status == 'success')) {
    
    
                fileList.forEach((e,index) => {
    
    
                    if(index >= size) {
    
    
                        // 处理文件信息
                        this.fileList.push({
    
    
                            fileCode: e.response.data.code,
                            code: e.response.data.code,
                            fileName: e.response.data.fileName,
                            filePath: e.response.data.filePath,
                            fileType: 1,
                            sourceName: e.response.data.sourceName,
                            size: (e.size / 1000).toFixed(1)
                        });
                    }
                });
                // 清除已上传的文件列表
                this.$refs.file.clearFiles();
                // 返回文件信息
                this.$emit('getFileInfo',this.fileList);
            }
        },
        // 上传失败
        uploadError(err) {
    
    
            // 暂时无法模拟
            this.loading = false;
            this.$message.warning('抱歉,上传失败,请稍后重试!');
        },
        // 处理点击事件
        handleClick(file) {
    
    
            let suffix = this.getFileSuffix(file,'filePath');
            if(['png','jpg','jpeg'].includes(suffix)) {
    
    
                // 图片直接查看
                this.fileSrc = file.filePath;
                this.openView = true;
            }else if(suffix == 'pdf') {
    
    
                // pdf预览
            }else{
    
    
                // 其他下载
            }
        },
        // 删除所有文件,一般用于弹窗关闭使用
        removeAllFile() {
    
    
            this.fileList = [];
            // 返回文件信息
            this.$emit('getFileInfo',[]);
        }
    }
};
</script>

<style scoped lang="scss">

.file-list{
    
    
    width: 700px;
    display: flex;
    flex-flow: wrap;
    .file-tag{
    
    
        cursor:pointer;
        margin-right: 10px;
        margin-bottom: 10px;
    }
}


.input-w{
    
    
    width: 500px !important;
}

.img-view{
    
    
    width: 380px;
    height: 380px;
    margin: 10px 20px;
}
</style>

照片墙版

效果图
请添加图片描述

<template>
    <div v-loading="loading" element-loading-text="拼命上传中"
         element-loading-spinner="el-icon-loading"
         element-loading-background="rgba(0, 0, 0, 0.8)">
        <el-row>
            <el-col :span="4">
                <el-form-item label="附件文档">
                    <el-upload ref="file" :data="params" :action="$uploadUrl" name="file" multiple :show-file-list="false" :limit="maxNumber" :file-list="fileList"
                               :before-upload="checkFiles" :on-exceed="handleExceed" :on-success="uploadSuccess" :on-error="uploadError">
                        <el-button size="small">选择文件</el-button>
                    </el-upload>
                </el-form-item>
            </el-col>
            <el-col :span="20">
                <!-- 使用插槽进行自定义 -->
                <slot name="uploadTip"></slot>
                <slot name="default">
                    <el-form-item label="附件">
                        <el-input :value="'支持PDF、JPEG、PNG、JPG、word、Excel、PPT等格式'" disabled></el-input>
                    </el-form-item>
                </slot>
            </el-col>
        </el-row>
        <el-row>
            <el-col :span="24">
                <el-form-item label="附件列表"></el-form-item>
                <div class="file-list">
                    <div v-for="(file,index) in fileList" :key="file.fileCode" class="file-item"
                         @mouseover="moveIndex=index" @mouseout="moveIndex=-1">
                        <!-- 缩略图 -->
                        <img :src="isImgOrPdf(file).url" />
                        <!-- 遮罩 -->
                        <div v-show="moveIndex==index" class="file-mask">
                            <!--
                                1、新增、修改时
                                         图片显示查看和删除按钮
                                         pdf显示查看和删除按钮
                                         其他格式显示下载和删除按钮
                                2、查看时
                                        图片、pdf显示查看
                                        其他格式显示下载
                             -->
                            <i v-if="isImgOrPdf(file).res" class="el-icon-zoom-in" @click="handleClick(file)"></i>
                            <i v-else v-download="({name:file.sourceName,url:file.filePath})" class="el-icon-download"></i>
                            <i v-if="flog!='view'" class="el-icon-delete" @click="handleRemove(file,index)"></i>
                        </div>
                    </div>
                </div>
            </el-col>
        </el-row>
        <!-- 图片预览 -->
        <el-dialog title="预览" :visible="openView" width="460px" :close-on-click-modal="false" append-to-body @close="openView=false">
            <el-image class="img-view" :src="fileSrc" fit="fill"></el-image>
        </el-dialog>
    </div>
</template>

<script>
export default {
    
    
    directives: {
    
    
        // 下载指令
        download: {
    
    
            inserted: (el,binding,vnode) => {
    
    
                el.onclick = () => {
    
    
                    let name = binding.value.name;
                    let url = binding.value.url;
                    let link = document.createElement('a');
                    link.setAttribute('download', name);
                    link.href = url;
                    document.body.appendChild(link); // 添加到页面中,为兼容Firefox浏览器
                    link.click();
                    document.body.removeChild(link); // 从页面移除
                };
            }
        }
    },
    props: {
    
    
        // 允许上传的文件裂隙
        fileType: {
    
    
            type: Array,
            default: () => ['pdf', 'jpg', 'jpeg', 'png', 'xls', 'xlsx', 'doc', 'docx','ppt']
        },
        // 最大文件上传数
        maxNumber: {
    
    
            type: Number,
            default: 4
        },
        // 上传时额外参数
        params: {
    
    
            type: Object,
            default: () => {
    
    }
        },
        // 用于回显
        fileList: {
    
    
            type: Array,
            default: () => []
        },
        // 标识
        flog: {
    
    
            type: String,
            default: 'view'
        }
    },
    data() {
    
    
        return {
    
    
            fileSrc: '',
            openView: false,
            // 上传加载
            loading: false,
            // 记录鼠标移入的下标
            moveIndex: -1
        };
    },
    methods: {
    
    
        // 获取文件后缀
        getFileSuffix(file,name) {
    
    
            let fileName = file[name].toLocaleLowerCase();
            return fileName.substr(fileName.lastIndexOf('.') + 1);
        },
        // 确定文件格式
        checkFiles(file) {
    
    
            // 文件后缀
            let suffix = this.getFileSuffix(file,'name');
            if(!this.fileType.includes(suffix)) {
    
    
                this.$message('格式错误,请按提示选择正确的文件格式');
                // before-upload 必须返回false,否则还会继续上传
                return false;
            }
            this.loading = true;
        },
        // 文件超限提醒
        handleExceed(files, fileList) {
    
    
            this.$message.warning(`当前限制选择 ${
      
      this.maxNumber} 个文件,本次选择了 ${
      
      files.length} 个文件,共选择了 ${
      
      files.length + fileList.length} 个文件`);
        },
        // 文件移除
        handleRemove(file, index) {
    
    
            this.$confirm('确定删除?', '提示', {
    
    
                confirmButtonText: '确定',
                showCancelButton: '取消',
                type: 'warning'
            }).then(() => {
    
    
                this.fileList.splice(index, 1);
                // 返回文件信息
                this.$emit('getFileInfo',this.fileList);
            }).catch(() => {
    
    
                this.$message({
    
     type: 'info', message: '已取消删除' });
            });
        },
        // 上传成功
        uploadSuccess(response, file, fileList) {
    
    
            this.loading = false;
            // 这里存在一个问题,当第二次上传时,之前上传的文件也会存在fileList中,但是之前的文件信息不存在response属性
            // 解决方式:跳过之前的文件信息判断
            let size = this.fileList.length; // 获取之前的文件个数
            if(fileList.every(e => e.status == 'success')) {
    
    
                fileList.forEach((e,index) => {
    
    
                    if(index >= size) {
    
    
                        // 处理文件信息
                        this.fileList.push({
    
    
                            fileCode: e.response.data.code,
                            code: e.response.data.code,
                            fileName: e.response.data.fileName,
                            filePath: e.response.data.filePath,
                            fileType: 1,
                            sourceName: e.response.data.sourceName,
                            size: (e.size / 1000).toFixed(1)
                        });
                    }
                });
                // 清除已上传的文件列表
                this.$refs.file.clearFiles();
                // 返回文件信息
                this.$emit('getFileInfo',this.fileList);
            }
        },
        // 上传失败
        uploadError(err) {
    
    
            // 暂时无法模拟
            this.loading = false;
            this.$message.warning('抱歉,上传失败,请稍后重试!');
        },
        // 查看
        handleClick(file) {
    
    
            let suffix = this.getFileSuffix(file,'filePath');
            if(['png','jpg','jpeg'].includes(suffix)) {
    
    
                // 图片直接查看
                this.fileSrc = file.filePath;
                this.openView = true;
            }else if(suffix == 'pdf') {
    
    
                // pdf预览
                window.open(file.filePath);
            }
        },
        // 删除所有文件,一般用于弹窗关闭使用
        removeAllFile() {
    
    
            this.fileList = [];
            // 返回文件信息
            this.$emit('getFileInfo',[]);
        },
        // 判断是否是图片或pdf
        isImgOrPdf(file) {
    
    
            let imgUrl = {
    
    
                'pdf': require('../images/pdf.png'),
                'ppt': require('../images/ppt.png'),
                'pptx': require('../images/pdf.png'),
                'docx': require('../images/word.png'),
                'doc': require('../images/word.png'),
                'xls': require('../images/excel.png'),
                'xlsx': require('../images/excel.png'),
                'dwg': require('../images/dwg.png'),
                'zip': require('../images/zip.png'),
                'rar': require('../images/zip.png'),
                '7z': require('../images/zip.png')
            };
            let suffix = this.getFileSuffix(file,'filePath');
            if(['png','jpg','jpeg'].includes(suffix)) {
    
    
                return{
    
    
                    url: file.filePath,
                    res: true
                };
            }else{
    
    
                return{
    
    
                    url: imgUrl[suffix],
                    res: false
                };
            }
        }
    }
};
</script>

<style scoped lang="scss">

.file-list{
    
    
    width: 700px;
    display: flex;
    flex-flow: wrap;
    .file-tag{
    
    
        cursor:pointer;
        margin-right: 10px;
        margin-bottom: 10px;
    }

    .file-item{
    
    
        width: 80px;
        height: 80px;
        border-radius: 10px;
        margin-right: 10px;
        cursor:pointer;
        position: relative;

        .file-mask{
    
    
            width: 80px;
            height: 80px;
            border-radius: 10px;
            background: black;
            opacity: 0.6;
            position: absolute;
            z-index: 200;
            display: flex;
            align-items: center;
            justify-content: space-evenly;
            color: #ffffff;
            font-size: 24px;
        }

        img{
    
    
            width: 80px;
            height: 80px;
            border-radius: 10px;
            position: absolute;
        }
    }
}


.input-w{
    
    
    width: 500px !important;
}

.img-view{
    
    
    width: 380px;
    height: 380px;
    margin: 10px 20px;
}
</style>

猜你喜欢

转载自blog.csdn.net/weixin_41897680/article/details/124746824