目录
- 一、代码实现([具体配置文档](https://element.eleme.cn/#/zh-CN/component/upload))))
-
- [1. 默认图片上传](#1. 默认图片上传)
- [2. 自定义图片上传](#2. 自定义图片上传)
- [3. 默认文件上传](#3. 默认文件上传)
- 二、效果图
一、代码实现(具体配置文档)
1. 默认图片上传
-
适用于:文件上传接口只要求file二进制文件,无需其他参数。(或者配置data属性用于上传时附带的额外参数)。
-
该实现方式会在选择完图片后就根据配置好的action的接口上传地址自动上传图片。
-
重点就是配置好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>
</template> <script> //获取实际项目的token // import { getToken } from '@/utils/auth'<!-- 上传提示 --> <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>
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) : '' } }
}
<style scoped> /* .el-upload--picture-card 控制加号部分 */
</script>/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. 自定义图片上传
-
不需要配置action,使用http-request 覆盖默认的上传行为,可以自定义上传的实现。适用于文件上传需要file文件和其他参数。
-
或者在http-request的方法中不做任何操作,在before-upload上传前的校验中,保存将要上传的文件file,然后页面根据实际需要,调用方法上传图片。(如在页面中加一个提交按钮,点击调用提交方法上传图片)。
-
重点简单了解FormData 的使用和Content-Type的类型
// 可以根据后台接口要求来决定参数的类型
onChange() {
//通常文件上传是要用 FormData 格式的
this.formdata = new FormData()
this.formdata.append('file', this.file)
this.formdata.append('name', this.name)
},
// this.formdata 就是要传给后台的参数了 -
multipart/form-data支持文件上传的格式,一般需要上传文件的表单则用该类型。
// 头像上传
<template>
// export function uploadAvatar(data) {
// return request({
// url: '/manager/user/uploadAvatar',
// method: 'post',
// data: data,
// headers: {
// 'Content-Type': 'multipart/form-data'
// }
// })
// }<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> <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; }请上传 <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>
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) => { // ...................... // }); },
},
<style scoped> /* .el-upload--picture-card 控制加号部分 */
};
</script>/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. 默认文件上传
-
和上述图片上传是一样的原理,只不过是限制的类型不一样。
<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> <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"], },请上传 <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>
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) : ""; },
},
<style scoped> .upload-file-uploader { margin-bottom: 5px; }
};
</script>.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>
二、效果图
-