在使用elementUI的Upload上传组件中,发现了一些问题,比如并不需要上传过程中的动画效果,如果想去掉,组件并没有提供简单的参数设置,需要自己添加css去掉动画效果。
.el-upload-list__item {
transition: none;
}
.el-list-enter-to,
.el-list-leave-to,
.el-list-enter-active,
.el-list-leave-active,
.el-list-move,
.is-ready {
display: none;
}
但是如果想要自定义已上传图片的删除按钮,或者需要自定义缩略图长宽比时,就发现根本改不了,因此需要自己动手图片上传组件。
1、支持自定义缩略图大小。
2、支持自定义上传图片大小。
3、支持自定义图片格式。
4、支持自定义图片展示效果(目前使用了v-viewer)。
5、支持图片拖拽排序。
HTML部分:
<template>
<div ref="uploadWrap" class="upload-wrap">
<div class="images-viewer" v-viewer>
<div
class="image-li"
v-for="(file, index) in pictureCard.fileList"
:style="`height: ${thumbnailHeight + 2}px`"
:key="index"
>
<i class="icon-delete-bg"></i>
<i
class="icon-delete el-icon-error"
@click="handleRemove(file, index)"
></i>
<img
:width="thumbnailWidth"
:height="thumbnailHeight"
draggable="true"
@dragend="e => handleDragEnd(e, index)"
@dragstart="e => handleDragStart(e, index)"
:data-source="file.url"
:src="
`${file.url}?x-oss-process=image/resize,m_fixed,h_${thumbnailHeight},w_${thumbnailWidth}/sharpen,100`
"
/>
</div>
</div>
<div
class="progress-wrap"
v-show="isUpload"
:style="
`padding-top: ${(thumbnailHeight - thumbnailWidth) / 2}px;
height: ${thumbnailHeight + 2}px`
"
>
<el-progress
:width="
thumbnailWidth > thumbnailHeight ? thumbnailHeight : thumbnailWidth
"
type="circle"
:percentage="uploadPercentage"
></el-progress>
</div>
<div
class="upload-plus"
v-show="!isUpload"
@click="handleAdd"
v-if="pictureCard.fileList.length < limit"
:style="
`width: ${thumbnailWidth}px;height: ${thumbnailHeight}px;line-height: ${thumbnailHeight}px`
"
>
<i class="el-icon-plus icon-add"></i>
<input
type="file"
name="file"
ref="inputFile"
:multiple="this.multiple"
:accept="this.accept"
@change="handleChange"
class="upload-wrap-input"
/>
</div>
</div>
</template>
JS部分(图片上传接口使用了阿里云OSS):
<script type="text/ecmascript-6">
export default {
name: 'Upload',
components: {},
props: {
// 文件大小限制
fileSize: {
type: Number,
default: 50
},
// 是否支持多选
multiple: {
type: Boolean,
default: false
},
// 缩略图尺寸宽
thumbnailWidth: {
type: Number,
default: 100
},
// 缩略图尺寸高
thumbnailHeight: {
type: Number,
default: 100
},
// 可上传的图片类型
accept: {
type: String,
default: "image/jpeg,image/jpg,image/png,,image/gif"
},
// 展示方式
type: {
type: String,
default: "picture-card"
},
// 图片限制数量
limit: {
type: Number,
default: 1
},
// 上传列表
fileLists: {
type: Array,
default: () => {
return []
}
}
},
data () {
return {
startX: 0,
startY: 0,
uploadPercentage: 0,
isUpload: false,
pictureCard:{
fileList: [],
},
uploadQueue: []
}
},
watch: {
// fileLists异步更新
fileLists(){
this.pictureCard.fileList = this.fileLists
},
// 处理批量上传
uploadQueue(){
if(this.uploadQueue.length > 0){
this.uploadFunc()
}
}
},
mounted () {
if(this.type === 'picture-card'){
this.pictureCard.fileList = this.fileLists
}
},
methods: {
// 开始拖拽图片
handleDragStart(e, index){
this.startX = e.x
this.startY = e.y
},
// 结束拖拽图片
handleDragEnd(e, index){
const moveX = e.x - this.startX
const moveY = e.y - this.startY
const width = this.thumbnailWidth + 10
const height = this.thumbnailHeight + 10
let wrapWidth = 0
if(this.$refs.uploadWrap){
wrapWidth = this.$refs.uploadWrap.offsetWidth
}else{
return
}
const rowNum = Math.floor(wrapWidth/width)
let numX = moveX > 0 ? Math.round(moveX/width) : Math.abs(Math.floor(moveX/width))
let numY = moveY > 0 ? Math.round(moveY/height) : Math.ceil(moveY/height)
let insertIndex = index
if(index < rowNum && numX < rowNum && numY < 0){
return
}
if(moveX > 0 && numX > 0){
insertIndex += numX - 1
}else if(moveX <= 0 && numX > 0){
insertIndex -= numX
}
insertIndex += rowNum * numY
this.pictureCardMove(index, insertIndex)
},
// 图片位置移动
pictureCardMove(index, insertIndex){
insertIndex = insertIndex < 0 ? 0 : insertIndex
insertIndex = insertIndex > this.pictureCard.fileList.length ? this.pictureCard.fileList.length : insertIndex
const item = this.pictureCard.fileList.splice(index, 1)[0]
this.pictureCard.fileList.splice(insertIndex, 0, item)
},
// 上传文件
handleChange(file){
if(file.target && file.target.files){
if(file.target.files.length > this.limit + this.pictureCard.fileList.length){
this.handleExceed()
return
}
let totalLth = 0
let $file = {}
for( let i = 0 ; i < file.target.files.length ; i ++){
totalLth = this.pictureCard.fileList.length + this.uploadQueue.length
$file = file.target.files[i]
if(totalLth < this.limit){
if($file.size > this.fileSize * 1024 * 1024){
this.$message({
type: 'error',
message: `文件“${$file.name}”大小超过${this.fileSize}M,不能上传`
})
}else{
this.uploadQueue.push({file : $file})
}
}
}
}
},
// 触发上传
handleAdd(){
this.$refs.inputFile.dispatchEvent(new MouseEvent('click'))
},
// 图片上传方法
uploadFunc(index = 0){
const upload = new Promise((resolve,reject) =>{
if(!this.uploadQueue[index] || index > this.limit - 1) return
// eslint-disable-next-line no-new
// 图片上传方法
uploadOSSFunc({
fileData: [this.uploadQueue[index].file],
fileSize: this.fileSize,
onSuccess: (response) => {
this.pictureCard.fileList.push({
name: data.file.name,
url: data.url,
thumbnail: `${response.url}?x-oss-process=image/resize,m_fixed,h_${this.thumbnailHeight},w_${this.thumbnailWidth}/sharpen,100`,
original : data.url
})
})
})
})
upload.then(() => {
if(this.uploadQueue.length - 1 === index){
this.returnData()
this.uploadQueue = []
if(this.$refs.inputFile && this.$refs.inputFile.value) {
this.$refs.inputFile.value = null
}
}
this.uploadFunc(index + 1)
})
},
// 删除图片
handleRemove(file, index) {
this.pictureCard.fileList.splice(index, 1)
this.returnData()
},
// 图片超出限制
handleExceed() {
this.$message.error(`当前只允许上传 ${this.limit} 个文件`);
},
// 返回数据
returnData(){
const resArr = this.pictureCard.fileList.map(item => {
return {
url: item.original,
original: item.original
}
})
this.$emit('update:fileLists', resArr)
}
}
}
</script>
CSS部分:
<style scoped lang="scss" rel="stylesheet/scss">
.upload-wrap {
padding: 0;
overflow: hidden;
background: #fff;
.progress-wrap {
margin-right: 10px;
display: inline-block;
vertical-align: top;
}
.upload-plus {
vertical-align: top;
display: inline-block;
border: 1px dotted #d9d9d9;
text-align: center;
line-height: 100%;
border-radius: 5px;
cursor: pointer;
background: #fbfdff;
i.icon-add {
font-size: 20px;
color: #999;
}
.upload-wrap-input {
display: none;
}
}
.images-viewer {
display: inline;
vertical-align: top;
.image-li {
float: left;
margin: 0 10px 10px 0;
position: relative;
i.icon-delete {
position: absolute;
top: 10px;
right: 10px;
color: #fd4e4d;
font-size: 18px;
cursor: pointer;
}
i.icon-delete-bg {
position: absolute;
top: 15px;
right: 14px;
font-size: 18px;
width: 10px;
height: 10px;
background: #fff;
border-radius: 10px;
}
}
img {
border-radius: 4px;
border: 1px dotted #d9d9d9;
}
}
}
</style>