前言
大家好,这里是藤原豆腐店,文件上传在项目中的使用频率很高,我这里对原来项目中的文件上传组件进行优化,使他支持上传多个文件,同时又不影响原来单文件上传的功能
相关配置项和数据处理
props: {
// 支持的格式
allowType:{
type:String,
default:'txt/xlsx/xls/docx/doc'
},
// 单文件上传
popFileUrl:{
type:String,
default:""
},
popFileName:{
type:String,
default:""
},
// 多文件上传
multiFileList:{
type:Array,
default:() => ([])
},
// 文件最大大小
maxSize:{
type:Number,
default:20
},
// 按钮文案
title:{
type:String,
default:"点击上传文档"
},
// 是否显示图标
isShowIcon:{
type:Boolean,
default:false
},
// 是否显示蓝色图标
isShowBlueIcon:{
type:Boolean,
default:false
},
// 是否显示提示
showTip:{
type:Boolean,
default:true
},
// 是否获取上传文件
getFile:{
type:Boolean,
default:false
},
// 按钮的宽度
width:{
type:Number,
default:192
},
// 按钮的高度
height:{
type:Number,
default:40
},
// 上传文件的数量
fileNumber:{
type:Number,
default:5
}
},
data(){
return {
fileUrl:'',//文件地址
fileName:'',//文件名称
uploadErr:false,//是否上传失败
tooltip:'',//提示文字
disabled:false//是否禁用上传
}
},
watch:{
popFileUrl: {
handler(nVal) {
this.fileUrl = nVal
},
immediate: true,
deep:true
},
popFileName: {
handler(nVal) {
this.fileName = nVal
},
immediate: true
},
fileNumber:{
handler(nVal) {
this.tooltip = `最多支持上传${nVal}个附件`
},
immediate: true
},
multiFileList:{
handler(nVal) {
// 判断是否超出附件数量
nVal.length>=this.fileNumber?this.disabled=true:this.disabled=false
},
immediate: true
}
},
文件上传功能实现
<el-upload
:before-upload="checkFile"
:on-success="uploadSuccess"
action="https://upa.pcauto.com.cn/upload_quick.jsp?application=autopocket&referer=https://priceadmin.pcauto.com.cn:7100/"
v-show="!fileUrl && multiFileList.length==0"
:limit="fileNumber"
multiple
>
<span class="el-button el-button__uploadfile" :class="isShowBlueIcon ? 'blue-btn' : ''" :style="{width:`${width}px`,height:`${height}px` }">
<img src="@/assets/img/icon-upload.png" v-if="isShowIcon && !isShowBlueIcon"/>
<img src="@/assets/img/upload-icon.png" v-if=" !isShowIcon && isShowBlueIcon"/>
{{title}}
</span>
<div slot="tip" class="el-upload__tip"> <span v-if="showTip">支持上传{{allowType}}格式,文件大小请控制在20M以内</span> <span
style="color:red;margin-left: 10px;" v-if="uploadErr"
><i class="el-icon-warning"></i>上传失败,请重新上传</span></div>
</el-upload>
上传文件前检查文件是否符合要求
checkFile(e){
let file = e
let size = file.size / 1024 // 转为kb
// 检查文件格式
let type = file.name.split('.').pop()//获取文件格式
let allowFileType = this.allowType.split('/')//支持文件格式
if(!allowFileType.includes(type)) {
this.$message(`文件类型只支持${this.allowType}, 请重新上传文件`)
return false
}
// 检测文件大小
let fileMaxSize = this.maxSize * 1024
if (size > fileMaxSize) {
this.$message(`文件不能超过${this.maxSize}M`)
return false;
}
// 图片转 base64 位
if(type=='png' || type=='PNG' ||type=='jpg' || type=='JPG' ){
this.toBase64(file)
return false;
}
// 传递文件给父组件
if(this.getFile){
this.$emit('getFileObj',file)
}
}
图片上传做特殊处理
/* 图片转 base64 位*/
async toBase64(e) {
const self = this;
let file = e;
let reader = new FileReader();
// 获取文件类型
let fileType = file.name.split('.').pop()
// 读取文件并将其转换为 base64
reader.readAsDataURL(file);
//文件读取完成时触发
reader.onload = async (e1)=> {
let image = new Image()
// 设置图片源为 base64 数据
image.src = e1.target.result
// 加载图片后的操作
let resData = await self.loadImage(image)
// 设置图片的 URL
self.urlPic = e1.target.result
// 将文件上传到阿里云
let res = await self.aliyunUploadImg(fileType,file)
if(res.retCode==0){
let fileImg = {}
// 在上传的文件列表中找到原始图片
res.files.forEach(item => {
if (item.isorg==1) {
fileImg = item
}
})
let fileUrl=""
// 根据是否为小程序选择不同尺寸的 URL
if(this.isMini){
fileUrl = fileImg.url.slice(0,fileImg.url.lastIndexOf(".")) +'_180x320'+fileImg.url.slice(fileImg.url.lastIndexOf("."),fileImg.url.length)
}else{
fileUrl = fileImg.url
}
// 替换 URL 中的 http 为 https
this.fileUrl = fileUrl.replace('http:','https:')
// 设置文件名
this.fileName = fileImg.name
// 发送文件信息给父组件
this.$emit('getFileObj',{
fileUrl: fileUrl.replace('http:','https:'),
fileName: fileImg.name
})
}else{
// 弹出上传失败的提示框
this.$confirm('图片上传失败', '', {
confirmButtonText: '确定',
showCancelButton:false,
});
}
}
}
// 返回图像的宽高
async loadImage(imgObj){
return new Promise( async (resolve,reject)=>{
imgObj.onload = ()=>{
let obj = {
width:imgObj.width,
height:imgObj.height
}
resolve(obj)
}
})
}
/* 阿里云OCC 上传 获取url*/
async aliyunUploadImg(fileType,file){
const params ={
fileExt : fileType || 'jpg',
appCode,
watermarkProcess:0
}
let res = await getUploadVoucher(Qs.stringify(params));
// 图片数据
let imgFile = {
retCode: -1,
files: []
}
if(res.status==200 && res.data){
const formData = new FormData()
formData.append('key', res.data.key)
formData.append('policy', res.data.policy)
formData.append('OSSAccessKeyId', res.data.ossAccessKeyId)
formData.append('success_action_status', 200)
formData.append('signature', res.data.signature)
formData.append('size', file.size)
formData.append('file', file)
let resImg = await getUploadAliYun(res.data.hostUrl.replace('http:','https:'), formData);
if(resImg.status==200&& resImg.data==""){
// 获取图片信息
let resinfo = await getAliYunImgInfo(`${res.data.publicUrl.replace('http:','https:')}?x-oss-process=image/info`)
console.log('resinfo',res.data);
imgFile.retCode = 0
imgFile.files.push({
isorg: 1,
url: res.data.publicUrl,
orgFileName: 'ali_' + res.data.imageId,
name: file.name,
width: resinfo.data.ImageWidth.value || '',
height: resinfo.data.ImageHeight.value || ''
})
}
return imgFile
}else{
this.$confirm('签名获取失败,请重新上传', '提示信息', {
type: 'error',
showCancelButton:false,
showClose:false,
});
return imgFile
}
},
图片上传成功
uploadSuccess(e){
if(e.retCode==0){
this.fileUrl=e.files[0].url
this.fileName=e.files[0].orgFileName
this.uploadErr = false
if(this.getFile)return
this.$emit('getFileObj',{
fileUrl: e.files[0].url,
fileName: e.files[0].orgFileName
})
}else{
this.uploadErr = true
}
},
支持单文件
上传后通过点击重新上传,重新上传文件
<!-- 单文件上传 -->
<div class="upload-demo upload-video" v-else-if="fileUrl">
<!-- 已上传文件 -->
<div class="file-name fl" :class="isShowBlueIcon ? 'blue-btn' : ''">
<img src="@/assets/img/icon-att-link.png" v-if="isShowIcon || isShowBlueIcon"/>
<span> {{fileName}} </span>
<img src="@/assets/img/icon-delete-white.png" style="cursor: pointer;" v-if="isShowIcon || isShowBlueIcon" @click="deleteFile"/>
</div>
<!-- 重新上传 -->
<el-upload
:before-upload="checkFile"
:on-success="uploadSuccess"
action="上传地址"
v-show="fileUrl"
class="fl"
>
<span class="upload-again" :class="isShowBlueIcon ? 'blue' : ''">重新上传 </span>
</el-upload>
</div>
/deep/.upload-file-wrap .upload-demo .file-name{
@include font_color('input_font');
display: flex;
align-items: center;
height: 40px;
line-height: 40px;
padding: 0 20px 0 20px;
@include bg_color("table_th_bg");
border-radius: 4px;
img{
width: 16px;
height: 16px;
display: inline-block;
vertical-align: bottom;
margin-right: 4px;
}
img:last-child{
margin-left: 10px;
}
span{
max-width: 300px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
}
.fl {
float: left;
}
子组件内单文件删除
// 单文件删除
deleteFile(){
this.fileName = ''
this.fileUrl = ''
this.$emit('deleteFileObj')
},
父组件调用通过配置项定制文件大小,格式等
<el-form-item class="el-form-item__fileup upload-att" label="">
<uploadFiles
title="上传文件"
:showTip="false"
:maxSize="50"
:popFileName="queryform.attachmentName"
:popFileUrl="queryform.attachment"
allowType="doc/docx/xls/xlsx/ppt/pdf/jpg/png"
@getFileObj="getFileObj"
@deleteFileObj="deleteFileObj"
></uploadFiles>
</el-form-item>
通过两个自定义方法获取和删除文件
// 获取文件
getFileObj(obj) {
this.queryform.attachment = obj.fileUrl.replace("http:", "https:");
this.queryform.attachmentName = obj.fileName;
},
// 删除文件
deleteFileObj() {
this.queryform.attachment = "";
this.queryform.attachmentName = "";
},
支持多文件
到达文件上限时,禁用上传按钮同时移到上面会有提示
<!-- 多文件上传 -->
<div class="multifile" v-if="multiFileList.length!==0">
<!-- 已上传文件 -->
<div class="upload-demo upload-video">
<div v-for="(item,index) in multiFileList">
<div class="file-name fl" :class="isShowBlueIcon ? 'blue-btn' : ''" style="margin:0 10px 20px 0">
<img src="@/assets/img/icon-att-link.png" v-if="isShowIcon || isShowBlueIcon"/>
<span> {{item.attachmentName}} </span>
<img src="@/assets/img/icon-delete-white.png" style="cursor: pointer;" v-if="isShowIcon || isShowBlueIcon" @click="deleteFileList(index)"/>
</div>
</div>
</div>
<!-- 继续上传 -->
<div>
<el-upload
:before-upload="checkFile"
:on-success="uploadSuccess"
action="https://upa.pcauto.com.cn/upload_quick.jsp?application=autopocket&referer=https://priceadmin.pcauto.com.cn:7100/"
v-show="multiFileList.length!==0"
:disabled="disabled"
>
<el-tooltip :effect="curTheme == 'dark' ? 'dark' : 'light'" :content="tooltip" placement="bottom" :disabled="!disabled">
<span class="el-button" :class="[isShowBlueIcon ? 'blue-btn' : '',disabled ? 'el-button__uploadfile__keep_disable':'el-button__uploadfile__keep']">
<img src="@/assets/img/icon-upload.png" v-if="isShowIcon && !isShowBlueIcon && !disabled"/>
<img src="@/assets/img/icon-upload-grey.png" v-if=" isShowIcon && !isShowBlueIcon && disabled"/>
<img src="@/assets/img/upload-icon.png" v-if=" !isShowIcon && isShowBlueIcon"/>
<span class="upload_keep">继续上传</span>
</span>
</el-tooltip>
</el-upload>
</div>
</div>
设置不同状态下上传按钮的样式
/deep/.upload-file-wrap .el-button__uploadfile__keep{
@include font_color('input_font_orange');
@include bg_color("menuS_bg_main");
@include border_color('comm_font_main_active');
width: 100px;
height: 40px;
border-radius: 4px;
}
/deep/.upload-file-wrap .el-button__uploadfile__keep_disable{
@include bg_color("menuS_bg_main");
color: #999999;
border: 1px solid #999999;
width: 100px;
height: 40px;
border-radius: 4px;
}
多文件删除
// 多文件删除
deleteFileList(index){
this.fileName = ''
this.fileUrl = ''
this.$emit('deleteFileList',index)
},
父组件调用
<el-form-item class="el-form-item__fileup upload-att" label="">
<uploadFiles
title="上传文件"
:isShowIcon="true"
:showTip="false"
:maxSize="50"
:multiFileList="queryform.file"
@getFileObj="getFileObj"
allowType="doc/docx/xls/xlsx/ppt/pdf/jpg/png"
@deleteFileList="deleteFileList"
></uploadFiles>
</el-form-item>
// 获取上传文件
getFileObj(obj) {
const fileArr = {
attachment: obj.fileUrl.replace("http:", "https:"),
attachmentName : obj.fileName
}
this.queryform.file.push(fileArr)
}
// 删除文件
deleteFileList(index) {
this.queryform.file.splice(index,1)
},
结尾
这个组件还有很多缺漏,比如不支持一次性上传多个文件等,如果有什么建议欢迎在评论区交流,最后看到这里的朋友如果觉得文章还可以的话,可以给我点点赞吗,万分感谢!!