前几天在使用element-ui中的el-upload组件的时候,发现默认的上传方法想要跨域还有携带请求头,都是挺麻烦的,所以针对el-upload组件进行了二次封装,下面代码中的systemApi是我们项目中的文件上传api,
<!--
* @Author: yxc
* @Date: 2020-08-20 11:00:36
* @Descripttion: 文件上传
* @LastEditors: yxc
* @LastEditTime: 2020-09-15 15:44:12
-->
<template>
<div v-if="uploadType === 'img'" class="img-upload">
<el-upload
class="upload-demo"
action="#"
:multiple="multiple"
:show-file-list="showFileList"
:accept="fileType.toString()"
:before-upload="beforeUpload"
:on-preview="handlePictureCardPreview"
:on-remove="handleRemove"
:file-list="fileList"
:list-type="listType"
:http-request="uploadImg"
>
<template v-if="listType === 'picture-card' || listType === '' || listType === null">
<i class="el-icon-plus"></i>
</template>
<template v-else>
<el-button size="small" type="primary">{
{
buttenText }}</el-button>
</template>
<div v-if="isShowPromptText" slot="tip" class="el-upload__tip">{
{
promptText }}</div>
</el-upload>
<el-dialog :visible.sync="imgDialog">
<img width="100%" :src="dialogImageUrl" alt="" />
</el-dialog>
</div>
<div v-else-if="uploadType === 'onePicture'" class="img-upload">
<el-upload
class="avatar-uploader"
action="#"
:show-file-list="showFileList"
:accept="fileType.toString()"
:before-upload="beforeUpload"
:on-preview="handlePictureCardPreview"
:on-remove="handleRemove"
:http-request="uploadImg"
>
<img v-if="fileList.length > 0" :src="fileList && fileList[0].url" class="avatar">
<i v-else-if="value ===''" class="el-icon-plus avatar-uploader-icon" />
<i v-else class="el-icon-plus avatar-uploader-icon"></i>
</el-upload>
</div>
<div v-else class="file-upload">
<el-upload
class="upload-demo"
action="#"
:multiple="multiple"
:show-file-list="showFileList"
:accept="fileType.toString()"
:before-upload="beforeUpload"
:on-remove="handleRemove"
:file-list="fileList"
:list-type="listType"
:http-request="uploadImg"
>
<template v-if="listType === 'picture-card'">
<i class="el-icon-plus"></i>
</template>
<template v-else>
<el-button size="small" type="primary">{
{
buttenText }}</el-button>
</template>
<div v-if="isShowPromptText" slot="tip" class="el-upload__tip">{
{
promptText }}</div>
</el-upload>
</div>
</template>
<script>
import systemFileApi from '@/api/file/system-file'
export default {
name: 'FileUpload',
components: {
},
props: {
listType: {
// 对应elementui的upload组件中的listtype
type: String,
default: null
},
fileTag: {
// 文件标签 必填
type: String,
required: true
},
value: {
// 上传文件的列表
type: [Array, String],
required: true
},
uploadType: {
// 上传类型 文件 or 图片 or 单张图片
type: String,
default: 'img',
validator: function(value) {
return ['img', 'other', 'onePicture'].indexOf(value) !== -1
}
},
buttenText: {
// 上传按钮的文字
type: String,
default: '点击上传'
},
fileType: {
// 允许上传文件类型,字符串数组格式 例如['.jpg', '.png', '.jpeg', '.gif']
type: Array,
required: true
},
fileSize: {
// 允许上传文件大小
type: Number,
default: 5
},
multiple: {
// 是否允许多选
type: Boolean,
default: true
},
isShowPromptText: {
// 是否显示下面提示文字
type: Boolean,
default: true
},
showFileList: {
// 是否显示文件列表
type: Boolean,
default: true
}
},
data() {
return {
fileList: [], // 已经上传文件的列表 拼接了文件服务器地址的
imgDialog: false, // 图片查看弹窗是否显示
dialogImageUrl: '', // 图片弹窗中图片链接
promptText: '', // 提示信息
fileServer: '' // 文件服务器地址
}
},
watch: {
value: {
handler(newValue, oldValue) {
// 监听v-model绑定值value,value值改变时,随时改变自身fileList中值
if (newValue !== null && newValue !== '') {
if (this.multiple) {
// 图片多选,value为数组
this.fileList = newValue.map(item => {
return {
id: item.fileId,
name: item.fileName,
url: this.fileServer + item.fileUrl
}
})
} else {
// 单选,value为字符串
if (newValue.indexOf('http') >= 0) {
this.fileList = [{
id: '', name: '', url: newValue }]
} else {
this.fileList = [{
id: '', name: '', url: this.fileServer + newValue }]
}
}
} else {
// 如果value为空,设置fileList为空
this.fileList = []
}
},
deep: true
}
// value: function(newValue, oldValue) {
//
// }
},
created() {
},
async mounted() {
// 生成提示信息
this.promptText = '只能上传' + this.fileType.toString() + '文件,且大小不能超过' + this.fileSize + 'M'
// 获取文件服务器地址
this.fileServer = await this.$store.dispatch('app/getFileServer')
// 根据v-model绑定值,给fileList赋值
if (this.value !== null && this.value !== '') {
if (this.multiple) {
// 图片多选,value为数组
this.fileList = this.value.map(item => {
return {
id: item.fileId,
name: item.fileName,
url: this.fileServer + item.fileUrl
}
})
} else {
// 单选,value为字符串
if (this.value.indexOf('http') === 0) {
this.fileList = [{
id: '', name: '', url: this.value }]
} else {
this.fileList = [{
id: '', name: '', url: this.fileServer + this.value }]
}
}
} else {
// 如果value为空,设置fileList为空
this.fileList = []
}
},
methods: {
// 上传之前,检查文件类型和文件大小 如果是头像单文件上传,上传前检查是否已经上传头像,已上传,要删除
beforeUpload(file) {
/**
* 检查文件大小
*/
if (file.size / 1024 / 1024 > this.fileSize) {
this.$message({
type: 'error',
message: `文件大小超过${
this.fileSize}M`
})
return false
}
/**
* 检查文件类型 利用文件名的后缀名来判断文件类型
*/
var type = file.name.split('.')[file.name.split('.').length - 1]
if (this.fileType.indexOf('.' + type) < 0) {
this.$message({
type: 'error',
message: `文件类型错误`
})
return false
}
/**
* 头像单文件上传
*/
if (this.uploadType === 'onePicture') {
if (this.fileList.length > 0) {
this.handleRemove(this.fileList[0])
}
return true
}
},
// 上传图片到系统文件
uploadImg(file) {
systemFileApi.uploadInfo(file.file, this.fileTag, file.file.name).then(res => {
if (res.msg === 'success') {
/**
* 多选时,绑定值为对象数组格式[{fileId: 'xxx', fileName: 'xxx', fileUrl: 'xxx'}]
*/
if (this.multiple) {
var data = {
// 给highlinhtSData赋值
fileId: res.data.fileIdHexString,
fileName: res.data.fileOriginalName,
fileUrl: res.data.fileUrl
}
this.value.push(data)
this.$emit('afterFileup')
} else {
/**
* 单选时,绑定值为String格式,直接是fileUrl
*/
this.$emit('input', res.data.fileUrl)
}
// 给filelist添加已经上传成功的文件
var fileListItem = {
id: res.data.fileIdHexString,
name: res.data.fileOriginalName,
url: res.data.fullFileUrl,
status: 'success'
}
this.fileList.push(fileListItem)
}
})
},
// 删除图片 服务器中删除 fileList中删除 value中删除
handleRemove(file) {
if (file.status === 'success') {
systemFileApi.deleteByUrl(file.url.slice(this.fileServer.length)).then(res => {
if (res.msg === 'success') {
// 删除fileList中的文件
this.fileList = this.fileList.filter(item => item.url !== file.url)
/**
* 文件选择为多选文件时,删除v-model绑定值中的对应对象
*/
if (this.multiple) {
// 删除value中的文件 使用$emit('input'),因为这里是改变了原来数组
this.$emit(
'input',
this.value.filter(item => item.fileId !== file.id)
)
} else {
/**
* 文件选择为单选时,给v-model绑定值赋值为null
*/
this.$emit('inpur', null)
}
}
})
} else {
this.fileList = this.fileList.filter(item => item.id !== file.id)
}
},
handlePictureCardPreview(file) {
this.dialogImageUrl = file.url
this.imgDialog = true
}
}
}
</script>
<style lang="scss">
.avatar-uploader .el-upload {
border: 1px dashed #d9d9d9;
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
}
.avatar-uploader .el-upload:hover {
border-color: #409eff;
}
.avatar-uploader-icon {
font-size: 28px;
color: #8c939d;
width: 178px;
height: 178px;
line-height: 178px;
text-align: center;
}
.avatar {
width: 178px;
height: 178px;
display: block;
}
</style>
文件上传的api,一定要使用FormData格式
/**
* @Author: yxc
* @Date: 2020-08-19 15:29:15
* @LastEditors: yxc
* @Descripttion: 上传文件,并返回信息
* @param data FormData 二进制文件
* @param fileTag String 文件标签
* @param fileOriginalName String
* @return axios
*/
uploadInfo(file, fileTag, fileOriginalName) {
const data = new FormData()
data.append('file', file)
const params = new URLSearchParams()
params.append('fileTag', fileTag)
params.append('fileOriginalName', fileOriginalName)
return request({
url: '/file/system-files/upload/info',
method: 'post',
headers: {
'Content-Type': 'multipart/form-data'
},
data,
params: params
})
}
有不对的地方,欢迎大家做出指正