图片上传
<template>
<div class="image-upload-component" :class="{multiple: multiple}">
<input type="file" class="input-file" :disabled="disabled" :accept="accept" :multiple="multiple" ref="inputFile" @change="fileChange" />
<slot name="default">
<template v-for="(row, index) in images">
<div class="image-wrapper" :key="index">
<img :src="row.absolute" v-if="isSuccess(row.status)" />
<div class="failed" v-if="isError(row.status)">上传失败</div>
<div class="tools" v-if="isSuccess(row.status) || isError(row.status)">
<el-button type="text" size="mini" title="预览" icon="el-icon-search" v-if="isSuccess(row.status)" />
<el-button type="text" size="mini" title="上传" icon="el-icon-upload2" v-if="!multiple && !disabled" @click="$refs.inputFile.click()" />
<el-button type="text" size="mini" title="删除" icon="el-icon-delete" v-if="!disabled" @click="deleteFile(index)" />
</div>
<div class="loading" v-if="isWait(row.status)"><i class="el-icon-loading" /></div>
</div>
</template>
</slot>
<slot name="add" v-if="addShow">
<div :disabled="disabled" class="add-wrapper" @click="$refs.inputFile.click()">
<i class="el-icon-plus" />
</div>
</slot>
<slot name="tips">
<div class="upload-tips">
<span>大小不能超过1MB</span>
<span>图片尺寸为1:1比例</span>
<span>图片只能为jpeg、jpg、png格式</span>
</div>
</slot>
</div>
</template>
<script>
export default {
data (){
return {
UPLOAD_WAIT: 0, //等待
UPLOAD_ING: 1, //上传中
UPLOAD_SUCCESS: 2, //上传成功
UPLOAD_FAILED: 3, //上传失败
accept: '.jpeg,.jpg,.png',
accepts: ['image/jpg', 'image/jpeg', 'image/png'],
images: [], //
};
},
props: {
/**
* 图片列表
* @example @returns
* [{
* relative: '/xxx/xxx.png',
* absolute: 'https://xxx/xxx.png'
* }]
*/
value: {
type: Array,
required: true
},
// 上传保存的文件夹
folder: {
type: String,
required: true
},
// 是否支持多选
multiple: {
type: Boolean,
default: false
},
// 是否禁用
disabled: {
type: Boolean,
default: false
},
// 允许上传的文件大小,单位kb
size: {
type: Number,
default: 1024
},
// 最大允许上传个数
limit: {
type: Number,
default: 10
}
},
watch: {
value (arr){
if( arr && arr instanceof Array && arr.length ){
for (let i = 0; i < arr.length; i++) {
let exist = this.images.find(row => row.relative == arr[i].relative);
if( !exist ) {
arr[i].status = 2;
this.recombination(arr[i]);
};
};
} else {
this.images = [];
};
}
},
computed: {
// 是否等待上传
isWait ( status ){
return status => {
return this.UPLOAD_WAIT == status;
};
},
// 是否上传中
isUping ( status ){
return status => {
return this.UPLOAD_ING == status;
};
},
// 是否上传成功
isSuccess ( status ){
return status => {
return this.UPLOAD_SUCCESS === status;
};
},
// 是否上传失败状态
isError ( status ){
return status => {
return this.UPLOAD_FAILED === status;
};
},
// 在部分情况下显示上传按钮
addShow (){
let { images, multiple, limit, disabled } = this
, length = images.length;
if( disabled ) return false;
if( multiple && length < limit ) return true;
if( !multiple && !length ) return true;
return false;
}
},
methods: {
// 选择文件
fileChange (e){
let pomises = []
, { files } = e.target
, fileCount = files.length //本次选择的文件数
, vacancy = this.limit - this.images.length //剩余可上传文件数
, forNumber = vacancy > fileCount ? fileCount : vacancy;
for (let i = 0; i < forNumber; i++) {
pomises.push(this.validateFile(files[i]));
};
Promise.all(pomises).then( files => {
for (let i = 0; i < files.length; i++) {
this.beginUpload(files[i]).then( file => {
this.recombination(file);
this.emitData();
});
};
}).catch( msg => {
this.$message.warning( msg );
}).finally(e => {
this.clearInput();
});
},
// 验证文件是否符合规范
validateFile (file){
let image = new Image()
, isSize = (file.size / 1024) > this.size
, isAccept = this.accepts.includes(file.type);
return new Promise((resolve, reject) => {
if(!isAccept){
reject('只能上传 jpeg、jpg、png 格式图片');
};
if (isSize) {
reject(`图片大小不能超过${this.renderSize(this.size)}`);
};
image.onload = e => {
if((image.width / image.height) == 1){
resolve(file);
} else {
reject(`请上传1:1比例的图片`);
};
};
image.onerror = e => {
reject('图片加载失败,请重新选择');
};
image.src = window.URL.createObjectURL(file);
});
},
recombination (file){
this.multiple ? this.images.push(file) : (this.images = [file]);
},
beginUpload (file){
return new Promise((resolve, reject) => {
this.$alioss.upload({ file, folder: this.folder }).then(({ relative, absolute }) => {
resolve({ relative, absolute, status: this.UPLOAD_SUCCESS });
});
}).catch(e => {
resolve({ status: this.UPLOAD_FAILED });
});
},
// 清空文件选择器
clearInput (){
this.$refs.inputFile.value = '';
},
// 删除文件
deleteFile (index){
this.images.splice(index, 1);
this.emitData();
},
// 只提交上传成功的文件
emitData (){
let data = this.images.filter(row => row.status == this.UPLOAD_SUCCESS).map( row => {
let { relative, absolute } = row;
return { relative, absolute };
});
this.$emit('input', data);
this.$emit('change', data);
},
// 格式化文件大小
renderSize( value = null ){
if ( !value ) return "0B";
let srcsize = parseFloat(value)
, index = Math.floor(Math.log(srcsize) / Math.log(1024))
, size = srcsize / Math.pow(1024, index)
, unitArr = ["KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"]
size = size.toFixed(0);//保留的小数位数
return `${size}${unitArr[index]}`;
}
}
};
</script>
<style lang="scss">
.image-upload-component{
display: flex;
.input-file{
display: none;
}
.image-wrapper,
.add-wrapper{
width: 120px;
height: 120px;
display: block;
overflow: hidden;
border-radius: 4px;
box-sizing: border-box;
border: 1px solid #DCDFE6;
}
.image-wrapper{
display: inline-flex;
position: relative;
align-items: center;
justify-content: center;
img{
max-width: 100%;
max-height: 100%;
}
.tools,
.failed,
.loading{
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
color: #fff;
font-size: 2rem;
align-items: center;
position: absolute;
justify-content: center;
background: rgba(0, 0, 0, .5);
}
.failed{
color: #ccc;
font-size: 14px;
background: #fff;
}
.tools{
opacity: 0;
transition: all .3s;
pointer-events: none;
.el-button{
color: #fff;
font-size: 18px;
cursor: pointer;
&:hover{
transform: scale(1.2);
text-shadow: 0 0 1px #fff;
}
}
}
&:hover{
.tools{
opacity: 1;
pointer-events: auto;
}
}
}
.add-wrapper{
font-size: 2rem;
cursor: pointer;
color: #DCDFE6;
transition: all .3s;
align-items: center;
display: inline-flex;
border-style: dashed;
justify-content: center;
&:hover{
color: #409EFF;
border-color: #409EFF;
}
&[disabled]{
pointer-events: none;
}
}
.upload-tips{
flex: 1;
margin-left: 20px;
display: inline-block;
span{
color: #ccc;
font-size: 12px;
display: block;
line-height: 24px;
}
}
&.multiple{
display: block;
.image-wrapper{
float: left;
margin: 0 20px 20px 0;
&:last-child{
margin: 0;
}
}
.add-wrapper{
float: left;
margin: 0 20px 20px 0;
}
.upload-tips{
width: 100%;
float: left;
display: block;
margin: 0;
}
}
}
</style>
页面引用
import imageUpload from "@/components/image-upload";
components: { imageUpload },
<image-upload
v-model="goodslistPhoto"
multiple
:limit="20"
folder="goodslistPhoto"
/>
不求大富大贵,但求一生平凡

浙公网安备 33010602011771号