Element中Upload组件上传(图片和文件的默认上传以及自定义上传)

目录

一、代码实现(具体配置文档)

1. 默认图片上传

  1. 适用于:文件上传接口只要求file二进制文件,无需其他参数。(或者配置data属性用于上传时附带的额外参数)。

  2. 该实现方式会在选择完图片后就根据配置好的action的接口上传地址自动上传图片。

  3. 重点就是配置好action属性,以及限制类型和大小。

    <template>
    <el-upload :action="uploadImgUrl" list-type="picture-card" :on-success="handleSuccess" :before-upload="handleBefore" :limit="limit" :on-error="handleError" :on-exceed="handleExceed" name="files" :on-remove="handleRemove" :show-file-list="true" :headers="headers" :file-list="fileList" :on-preview="handlePreview" :class="{ hide: this.fileList.length >= this.limit }" :disabled="disabled" :data="uploadData" > </el-upload>
     	<!-- 上传提示 -->
     	<div v-if="showTip" slot="tip" class="el-upload__tip">
     		请上传
     		<template v-if="fileSize">
     			大小不超过 <b style="color: #f56c6c">{{ fileSize }}MB</b>
     		</template>
     		<template v-if="fileType">
     			格式为 <b style="color: #f56c6c">{{ fileType.join('/') }}</b>
     		</template>
     		的文件
     	</div>
    
     	<el-dialog
     		:close-on-click-modal="false"
     		:close-on-press-escape="false"
     		:visible.sync="isVisible"
     		title="预览"
     		width="800"
     		append-to-body
     	>
     		<img :src="dialogImageUrl" style="display: block; max-width: 100%; margin: 0 auto" />
     	</el-dialog>
     </div>
    
    </template> <script> //获取实际项目的token // import { getToken } from '@/utils/auth'

    export default {
    props: {
    // 选择的图片数据
    value: [String, Object, Array],
    // 图片数量限制 limit 最大允许上传个数
    limit: {
    type: Number,
    default: 10
    },
    // 大小限制(MB)
    fileSize: {
    type: Number,
    default: 5
    },
    // 文件类型, 例如['png', 'jpg', 'jpeg']
    fileType: {
    type: Array,
    default: () => ['png', 'jpg', 'jpeg']
    },
    // 是否显示提示
    isShowTip: {
    type: Boolean,
    default: true
    },
    // 是否禁用
    disabled: {
    type: Boolean,
    default: false
    }
    },
    data() {
    return {
    // 预览图片地址
    dialogImageUrl: '',
    // 预览弹框是否显示
    isVisible: false,
    // hideUpload: false,
    // 接口地址
    baseUrl: process.env.VUE_APP_BASE_API,
    // 必选参数,上传的地址
    uploadImgUrl: process.env.VUE_APP_BASE_API + '图片服务器地址', // 上传的图片服务器地址
    // headers 设置上传的请求头部
    headers: { //获取实际token后,取消掉注释
    // Authorization: 'Bearer ' + getToken()
    },
    // file-list 上传的文件列表, 例如: [{name: 'food.jpg', url: 'https://xxx.cdn.com/xxx.jpg'}] array --- []
    fileList: [],
    // 上传时附带的额外参数
    uploadData: { toPdf: false },
    }
    },
    computed: {
    // 是否显示提示
    showTip() {
    return this.isShowTip && (this.fileType || this.fileSize)
    }
    },
    watch: {
    value: {
    handler(val) {
    // console.log(val,'chak')
    if (val) {
    // 1.将值转为数组
    const list = Array.isArray(val) ? val : this.value.split(',')
    console.log(list)
    // 2.然后将数组转为对象数组
    this.fileList = list.map(item => {
    if (typeof item === 'string') {
    if (item.indexOf(this.baseUrl) === -1) {
    item = {
    name: item,
    url: item
    }
    } else {
    item = {
    name: item,
    url: item
    }
    }
    }
    return item
    })
    } else {
    this.fileList = []
    return []
    }
    },
    deep: true,
    immediate: true
    }
    },
    methods: {
    // 删除图片
    // on-remove 文件列表移除文件时的钩子 function(file, fileList)
    handleRemove(file) {
    const findex = this.fileList.map(f => f.name).indexOf(file.name)
    this.fileList.splice(findex, 1)
    // 获取到由所有文件的url地址组成的逗号分割的字符串
    this.emit('input', this.getString(this.fileList)) // 获取到选择的所有文件的所有信息 this.emit('getInput', JSON.stringify(this.fileList))
    },
    // 上传成功回调
    // 文件上传成功时的钩子 function(response, file, fileList)
    handleSuccess(res) {
    if (res && res.data && res.data[0]) {
    const result = res.data[0]
    this.fileList.push({
    name: result.originalName,
    url: result.fileUrl,
    id: result.id
    })
    // 获取到由所有文件的url地址组成的逗号分割的字符串
    this.emit('input', this.getString(this.fileList)) // 获取到选择的所有文件的所有信息 this.emit('getInput', JSON.stringify(this.fileList))
    console.log(this.getString(this.fileList), 'res')
    console.log(JSON.stringify(this.fileList), 'res')
    }
    this.loading.close()
    },
    // 上传前loading加载
    // before-upload上传文件之前的钩子,参数为上传的文件,
    // 若返回 false 或者返回 Promise 且被 reject,则停止上传。 function(file)
    handleBefore(file) {
    console.log(file, 'handleBefore')
    let isImg = false // 判断是否是图片
    if (this.fileType.length) {
    // 判断是否限制了类型
    let fileExtension = ''
    // 从后判断是否存在.,并且获取到他的下标,然后获取的上传的文件的后缀
    if (file.name.lastIndexOf('.') > -1) {
    fileExtension = file.name.slice(file.name.lastIndexOf('.') + 1)
    }
    isImg = this.fileType.some(type => {
    // 如果上传文件的类型type是规定的类型fileType中的某一个,返回true
    if (file.type.indexOf(type) > -1) return true
    // 如果上传文件的后缀是规定的类型fileType中的某一个,返回true
    if (fileExtension && fileExtension.indexOf(type) > -1) return true
    // 后缀返回false
    return false
    })
    } else {
    // 判断上传的文件的类型是否是图片
    isImg = file.type.indexOf('image') > -1
    }

     		// 如果不是图片则提示一下,且不可以上传
     		if (!isImg) {
     			this.$message.error(`文件格式不正确, 请上传${this.fileType.join('/')}图片格式文件!`)
     			return false
     		}
     		// 判断大小,B<KB<MB<GB,且1 kB = 1024 B ,1 MB = 1024 kB ,1 GB = 1024 MB。
     		// file.size 的单位是B
     		// bit就是位,也叫比特位,是计算机表示数据最小的单位 ; byte就是字节
     		//  1byte=8bit  ; 1byte就是1B
     		if (this.fileSize) {
     			const isLt = file.size / 1024 / 1024 < this.fileSize
     			if (!isLt) {
     				this.$message.error(`上传头像图片大小不能超过 ${this.fileSize} MB!`)
     				return false
     			}
     		}
     		this.loading = this.$loading({
     			lock: true,
     			text: '上传中',
     			background: 'rgba(0, 0, 0, 0.7)'
     		})
     	},
     	// 文件个数超出
     	// on-exceed	文件超出个数限制时的钩子	function(files, fileList)
     	handleExceed() {
     		this.$message.error(`上传文件数量不能超过 ${this.limit} 个!`)
     	},
     	// 上传失败
     	// on-error	文件上传失败时的钩子	function(err, file, fileList)
     	handleError() {
     		this.$message({
     			type: 'error',
     			message: '上传失败'
     		})
     		this.loading.close()
     	},
     	// 预览
     	// on-preview	点击文件列表中已上传的文件时的钩子	function(file)
     	handlePreview(file) {
     		this.dialogImageUrl = file.url
     		this.isVisible = true
     	},
     	// 对象转成指定字符串分隔 (比如获取到由所有文件的url地址组成的逗号分割的字符串)
     	getString(list, separator) {
     		let strs = ''
     		separator = separator || ','
     		for (const i in list) {
     			strs += list[i].url + separator
     		}
     		return strs != '' ? strs.substr(0, strs.length - 1) : ''
     	}
     }
    

    }
    </script>

    <style scoped> /* .el-upload--picture-card 控制加号部分 */

    /deep/ .hide .el-upload--picture-card {
    display: none;
    }

    /* 去掉动画效果 */
    /deep/ .el-list-enter-active,
    /deep/ .el-list-leave-active {
    transition: all 0s;
    }

    /deep/ .el-list-enter,
    .el-list-leave-active {
    opacity: 0;
    transform: translateY(0);
    }
    </style>

2. 自定义图片上传

  1. 不需要配置action,使用http-request 覆盖默认的上传行为,可以自定义上传的实现。适用于文件上传需要file文件和其他参数。

  2. 或者在http-request的方法中不做任何操作,在before-upload上传前的校验中,保存将要上传的文件file,然后页面根据实际需要,调用方法上传图片。(如在页面中加一个提交按钮,点击调用提交方法上传图片)。

  3. 重点简单了解FormData 的使用和Content-Type的类型

    // 可以根据后台接口要求来决定参数的类型
    onChange() {
    //通常文件上传是要用 FormData 格式的
    this.formdata = new FormData()
    this.formdata.append('file', this.file)
    this.formdata.append('name', this.name)
    },
    // this.formdata 就是要传给后台的参数了

  4. multipart/form-data支持文件上传的格式,一般需要上传文件的表单则用该类型。

    // 头像上传
    // export function uploadAvatar(data) {
    // return request({
    // url: '/manager/user/uploadAvatar',
    // method: 'post',
    // data: data,
    // headers: {
    // 'Content-Type': 'multipart/form-data'
    // }
    // })
    // }

    <template>
    <el-upload action="" list-type="picture-card" :before-upload="handleBefore" :limit="limit" :http-request="handleFileUpload" :on-error="handleError" :on-exceed="handleExceed" :on-remove="handleRemove" :show-file-list="true" :file-list="fileList" :on-preview="handlePreview" :class="{ hide: fileList.length >= limit }" :disabled="disabled" > </el-upload>
    请上传 <template v-if="fileSize"> 大小不超过 {{ fileSize }}MB </template> <template v-if="fileType"> 格式为 {{ fileType.join("/") }} </template> 的文件
     <el-dialog
       :close-on-click-modal="false"
       :close-on-press-escape="false"
       :visible.sync="dialogVisible"
       title="预览"
       width="800"
       append-to-body
     >
       <img
         :src="isVisible"
         style="display: block; max-width: 100%; margin: 0 auto"
       />
     </el-dialog>
    
    </template> <script> export default { props: { value: [String, Object, Array], // 图片数量限制 limit: { type: Number, default: 10, }, // 大小限制(MB) fileSize: { type: Number, default: 10, }, // 文件类型, 例如['png', 'jpg', 'jpeg'] fileType: { type: Array, default: () => ["png", "jpg", "jpeg"], }, // 是否显示提示 isShowTip: { type: Boolean, default: true, }, // 是否禁用 disabled: { type: Boolean, default: false, }, // 详情id peopleId: { type: String, default: () => "", }, }, data() { return { isVisible: "", dialogVisible: false, baseUrl: process.env.VUE_APP_BASE_API, fileList: [], // file:null, }; }, computed: { // 是否显示提示 showTip() { return this.isShowTip && (this.fileType || this.fileSize); }, }, watch: { value: { handler(val) { if (val) { // 1.将值转为数组 const list = Array.isArray(val) ? val : this.value.split(","); // 2.将数组转为对象数组 this.fileList = list.map((item) => { if (typeof item === "string") { if (item.indexOf(this.baseUrl) === -1) { item = { name: item, url: item, }; } else { item = { name: item, url: item, }; } } return item; }); } else { this.fileList = []; return []; } }, deep: true, immediate: true, }, }, methods: { // 删除图片 handleRemove(file) { const findex = this.fileList.map((f) => f.name).indexOf(file.name); this.fileList.splice(findex, 1); this.$emit("input", this.getString(this.fileList)); this.$emit("getInput", JSON.stringify(this.fileList)); }, // 上传成功回调 handleUploadSuccess(res) { console.log(res, "success"); const result = res.data[0]; this.fileList.push({ name: result.originalName, url: result.fileUrl, id: result.id, }); this.$emit("input", this.getString(this.fileList)); this.$emit("getInput", JSON.stringify(this.fileList)); console.log(this.getString(this.fileList), "res"); console.log(JSON.stringify(this.fileList), "res"); this.loading.close(); }, // 上传前loading加载 handleBefore(file) { // this.file = file let isImg = false; if (this.fileType.length) { let fileExtension = ""; if (file.name.lastIndexOf(".") > -1) { fileExtension = file.name.slice(file.name.lastIndexOf(".") + 1); } isImg = this.fileType.some((type) => { if (file.type.indexOf(type) > -1) return true; if (fileExtension && fileExtension.indexOf(type) > -1) return true; return false; }); } else { isImg = file.type.indexOf("image") > -1; }
       if (!isImg) {
         this.$message.error(
           `文件格式不正确, 请上传${this.fileType.join("/")}图片格式文件!`
         );
         return false;
       }
       if (this.fileSize) {
         const isLt = file.size / 1024 / 1024 < this.fileSize;
         if (!isLt) {
           this.$message.error(`上传头像图片大小不能超过 ${this.fileSize} MB!`);
           return false;
         }
       }
       this.loading = this.$loading({
         lock: true,
         text: "上传中",
         background: "rgba(0, 0, 0, 0.7)",
       });
     },
     // 文件个数超出
     handleExceed() {
       this.$message.error(`上传文件数量不能超过 ${this.limit} 个!`);
     },
     // 上传失败
     handleError() {
       this.$message({
         type: "error",
         message: "上传失败",
       });
       this.loading.close();
     },
     // 预览
     handlePreview(file) {
       this.isVisible = file.url;
       this.dialogVisible = true;
     },
     // 对象转成指定字符串分隔
     getString(list, separator) {
       let strs = "";
       separator = separator || ",";
       for (const i in list) {
         strs += list[i].url + separator;
       }
       return strs != "" ? strs.substr(0, strs.length - 1) : "";
     },
     // http-request	覆盖默认的上传行为,可以自定义上传的实现	function
     // 上传文件的文件流是无法被序列化并传递的。所以我们就可以使用到formdata对象,就可以轻松进行文件上传了。
     // 创建formdata对象实例的方式 :new FormData()
     // 添加数据:append(key,value) 如果key不存在会新增一条数据,如果key存在,则添加到数据末尾
     handleFileUpload(e) {
       console.log(e);
       // let _this = this
       let formdata = new FormData();
       formdata.append("file", e.file);
       console.log(formdata, "formdata");
       // 上传图片
       // uploadAvatar(formdata)
       // 	.then(res => {
       // 		console.log(res,'res')
       // ......
       // 		_this.loading.close()
       // 	})
       // 	.catch(() => {
       // 		_this.fileList = []
       // 		this.loading.close()
       // 	})
     },
     // 上传图片 (也可以在页面上面加一个提交的按钮,点击按钮调用这个方法进行上传,上述handleFileUpload方法中不写任何代码)
     uploadImg() {
       // let formData = new FormData();
       // name上传的文件字段名上传的文件字段名,默认是"file",具体根据接口来
       // this.files 在上传前的handleBefore方法中保存这个文件
       // formData.append("file", this.files);
       // uploadAvatar(formData).then((response) => {
       // ......................
       // });
     },
    

    },
    };
    </script>

    <style scoped> /* .el-upload--picture-card 控制加号部分 */

    /deep/ .hide .el-upload--picture-card {
    display: none;
    }

    /* 去掉动画效果 */
    /deep/ .el-list-enter-active,
    /deep/ .el-list-leave-active {
    transition: all 0s;
    }

    /deep/ .el-list-enter,
    .el-list-leave-active {
    opacity: 0;
    transform: translateY(0);
    }
    </style>

3. 默认文件上传

  1. 和上述图片上传是一样的原理,只不过是限制的类型不一样。

    <template>
    <el-upload :drag="drag" ref="upload" :action="uploadFileUrl" v-if="showDing" :before-upload="handleBefore" :file-list="fileList" :limit="limit" name="files" :on-error="handleError" :on-exceed="handleExceed" :on-success="handleSuccess" :show-file-list="false" :headers="headers" class="upload-file-uploader" :data="uploadData"> <el-button v-if="showDing" size="mini" type="primary">选取文件</el-button>
    请上传 <template v-if="fileSize"> 大小不超过 {{ fileSize }}MB </template> <template v-if="fileType"> 格式为 {{ fileType.join("/") }} </template> 的文件
    </el-upload>
     <!-- 文件列表 -->
     <transition-group class="transition-box"  tag="ol">
       <li v-for="(file, index) in fileList" :key="index" class="upload-item-box">
         <div class="upload-item">
           <div>
             <el-link>
               <span class="el-icon-document" style="margin-left: 10px">
                 {{ file.name }}
               </span>
             </el-link>
           </div>
           <div>
             <el-link :underline="false" class="look" type="primary" @click="handlePrew(file)">
               <span class="el-icon-view"> 查看 </span>
             </el-link>
             <div class="delBox" v-if="showDing">
               <el-link v-if="index > number - 1" class="el-icon-delete link" :underline="false" type="danger"
                 @click="handleDelete(index)">删除</el-link>
             </div>
           </div>
         </div>
       </li>
     </transition-group>
    
    </template> <script> // import { getToken } from "@/utils/auth"; export default { name: "FileUpload", props: { // 值 value: [String, Object, Array], // 数量限制 limit: { type: Number, default: 5, }, // 大小限制(MB) fileSize: { type: Number, default: 10, }, number: { type: Number, default: -1000, }, // 文件类型, 例如['png', 'jpg', 'jpeg'] fileType: { type: Array, default: () => ["png", "jpg", "jpeg", "doc", "xls", "ppt", "pdf"], },
     imageType: {
       type: Array,
       default: () => ["png", "jpg", "jpeg"],
     },
     // 是否显示提示上传提示
     isShowTip: {
       type: Boolean,
       default: true,
     },
    
     showDing: {
       type: Boolean,
       default: true,
     },
    
     drag: {
       type: Boolean,
       default: false,
     },
    

    },
    data() {
    return {
    loading: {},
    uploadData: { toPdf: false },
    baseUrl: process.env.VUE_APP_BASE_API,
    uploadFileUrl: process.env.VUE_APP_BASE_API + "图片服务器地址", // 上传的图片服务器地址
    headers: {
    // Authorization: "Bearer " + getToken(),
    },
    fileList: [],
    files: [],
    };
    },
    computed: {
    // 是否显示提示
    showTip() {
    return this.isShowTip && (this.fileType || this.fileSize);
    },
    },
    watch: {
    value: {
    handler(val) {
    if (val) {
    this.files = val;
    var that = this;
    let temp = 1;
    // 1.将值转为数组
    // this.fileList
    const list = Array.isArray(val) ? val : this.value.split(",");
    // 2.将数组转为对象数组
    this.fileList = list.map((item, index) => {
    if (typeof item === "string") {
    item = { url: item };
    }
    item.name = that.files[index].originalName;
    item.uid = item.uid || new Date().getTime() + temp++;
    return item;
    });
    } else {
    this.fileList = [];
    this.files = [];
    return [];
    }
    },
    deep: true,
    immediate: true,
    },
    },
    methods: {
    // 预览
    handlePrew(file) {
    window.open(file.fileUrl);
    },
    // 上传前校检格式和大小
    handleBefore(file) {
    // 校检文件类型
    if (this.fileType) {
    let fileExtension = "";
    if (file.name.lastIndexOf(".") > -1) {
    fileExtension = file.name.slice(file.name.lastIndexOf(".") + 1);
    }
    // fileExtension: jfif
    const isTypeOk = this.fileType.some((type) => {
    // 这里注意jfif图片 类型的解析 会在第一步直接解析成image/jpeg
    if (file.type.indexOf(type) > -1) return true;
    if (fileExtension && fileExtension.indexOf(type) > -1) return true;
    return false;
    });
    if (!isTypeOk) {
    this.message.error( `文件格式不正确, 请上传{this.fileType.join("/")}格式文件!`
    );
    return false;
    }
    // 判断一下,如果类型是this.imageType中的某一个,比如这里指的是图片类型,不需要转为pdf格式
    // this.uploadData.toPdf 这个是额外的数据,具体看实际项目以及和后端配合有关
    if (this.imageType) {
    let toPdf = true;
    this.imageType.some((type) => {
    // 这里注意jfif图片 类型的解析 会在第一步直接解析成image/jpeg
    if (file.type.indexOf(type) > -1) {
    toPdf = false;
    }
    if (fileExtension && fileExtension.indexOf(type) > -1) {
    toPdf = false;
    }
    });

           this.uploadData.toPdf = toPdf;
         }
       }
    
       // 校检文件大小
       if (this.fileSize) {
         const isLt = file.size / 1024 / 1024 < this.fileSize;
         if (!isLt) {
           this.$message.error(`上传文件大小不能超过 ${this.fileSize} MB!`);
           return false;
         }
       }
    
       this.loading = this.$loading({
         lock: true,
         text: "上传中",
         background: "rgba(0, 0, 0, 0.7)",
       });
       console.log(file, "file");
       return true;
     },
     // 文件个数超出
     handleExceed() {
       this.$message.error(`上传文件数量不能超过 ${this.limit} 个!`);
     },
     // 上传失败
     handleError() {
       console.log("handleError");
       this.$message.error("上传失败, 请重试");
       this.loading.close();
     },
     // 上传成功回调
     handleSuccess(res, file) {
       console.log("handleSuccess", res, file);
       if (res.data && res.data[0]) {
         const result = res.data[0];
         this.fileList.push({ id: result.id, name: result.originalName, url: result.fileUrl });
         this.files.push(result);
         this.$emit("getInput", this.fileList);
         this.$emit("input", this.files);
         // 获取由选择文件的id组成的以逗号分割的字符串
         this.$emit("getId", this.getString(this.fileList, ',', 'id'));
         this.loading.close();
       } else {
         this.$message.error(res.msg);
         this.loading.close();
       }
     },
     // 删除文件
     handleDelete(index) {
       this.fileList.splice(index, 1);
       this.files.splice(index, 1);
       this.$emit("getInput", this.fileList);
       this.$emit("input", this.files);
       this.$emit("getId", this.getString(this.fileList, ',', 'id'));
     },
     // 对象转成指定字符串分隔
     getString(list, separator, FieldName) {
       let strs = "";
       separator = separator || ",";
       for (const i in list) {
         strs += list[i][FieldName] + separator;
       }
       return strs != "" ? strs.substr(0, strs.length - 1) : "";
     },
    

    },
    };
    </script>

    <style scoped> .upload-file-uploader { margin-bottom: 5px; }

    .transition-box .upload-item-box{
    border: 1px solid #e4e7ed;
    line-height: 3;
    margin-bottom: 10px;
    position: relative;
    }

    .upload-item{
    width: 95%;
    display: flex;
    justify-content: space-between;
    }

    .delBox{
    display: inline-block;
    }

    .look {
    margin-left: 10px;
    margin-right: 10px;
    }
    </style>

二、效果图


相关推荐
Devil枫2 小时前
Vue 3 单元测试与E2E测试
前端·vue.js·单元测试
GIS程序媛—椰子3 小时前
【Vue 全家桶】6、vue-router 路由(更新中)
前端·vue.js
前端青山4 小时前
Node.js-增强 API 安全性和性能优化
开发语言·前端·javascript·性能优化·前端框架·node.js
毕业设计制作和分享4 小时前
ssm《数据库系统原理》课程平台的设计与实现+vue
前端·数据库·vue.js·oracle·mybatis
程序媛小果4 小时前
基于java+SpringBoot+Vue的旅游管理系统设计与实现
java·vue.js·spring boot
从兄5 小时前
vue 使用docx-preview 预览替换文档内的特定变量
javascript·vue.js·ecmascript
凉辰5 小时前
设计模式 策略模式 场景Vue (技术提升)
vue.js·设计模式·策略模式
清灵xmf6 小时前
在 Vue 中实现与优化轮询技术
前端·javascript·vue·轮询
薛一半7 小时前
PC端查看历史消息,鼠标向上滚动加载数据时页面停留在上次查看的位置
前端·javascript·vue.js
过期的H2O27 小时前
【H2O2|全栈】JS进阶知识(四)Ajax
开发语言·javascript·ajax