针对上传组件进行封装,在页面直接引用即可,上传到minio文件服务器:
可以预览,重新上传,只读模式,可以传入展示缩略图尺寸,传入上传校验尺寸
javascript
<template>
<div>
<div v-if="readOnly" class="avatar-uploader" :style="{ width: width + 'px', height: height + 'px' }">
<div v-if="imageUrl" class="image-container" @click.stop>
<img :src="`${baseUrl}/file/file/previewByCode/${imageUrl}`" class="avatar" :style="{ width: width + 'px', height: height + 'px' }">
<div class="icon-wrapper" :class="{ readonly: readOnly }">
<span class="icon-item preview" @click="handlePreview">
<el-tooltip
content="查看"
placement="top"
effect="dark"
>
<el-button type="primary" size="mini" class="el-icon-zoom-in"></el-button>
</el-tooltip>
</span>
</div>
</div>
<i v-else class="el-icon-plus avatar-uploader-icon"></i>
</div>
<el-upload
v-else
class="avatar-uploader"
:action="action"
:show-file-list="false"
:on-success="handleAvatarSuccess"
:before-upload="beforeAvatarUpload"
:style="{ width: width + 'px', height: height + 'px' }"
>
<div v-if="imageUrl" class="image-container" @click.stop>
<img :src="`${baseUrl}/file/file/previewByCode/${imageUrl}`" class="avatar" :style="{ width: width + 'px', height: height + 'px' }">
<div class="icon-wrapper" :class="{ readonly: readOnly }">
<span class="icon-item preview" @click="handlePreview">
<el-tooltip
content="查看"
placement="top"
effect="dark"
>
<el-button type="primary" size="mini" class="el-icon-zoom-in"></el-button>
</el-tooltip>
</span>
<span class="icon-item reupload" @click="handleReUpload">
<el-tooltip
content="重新上传"
placement="top"
effect="dark"
>
<el-button type="primary" size="mini" class="el-icon-upload"></el-button>
</el-tooltip>
</span>
</div>
</div>
<i v-else class="el-icon-plus avatar-uploader-icon"></i>
</el-upload>
<!-- 图片预览遮罩层 -->
<div v-show="previewVisible" class="image-preview-modal" @click="previewVisible = false">
<img :src="previewImage" alt="预览图" class="preview-image" @click.stop>
</div>
</div>
</template>
<script>
export default {
name: "ImageUpload",
data() {
return {
baseUrl: this.$global.baseUrl,
// 预览控制
previewVisible: false,
previewImage: ''
};
},
props: {
imageUrl: {
type: String,
default: ""
},
action: {
type: String,
default: ''
},
width: {
type: [String, Number],
default: 178
},
height: {
type: [String, Number],
default: 178
},
readOnly: {
type: Boolean,
default: false
},
// 图片尺寸校验
maxWidth: {
type: Number,
default: null
},
maxHeight: {
type: Number,
default: null
},
},
methods: {
handleAvatarSuccess(res, file) {
const data = res.data || res;
let imageUrl = '';
if (typeof data === 'string') {
imageUrl = data;
} else if (data.url) {
imageUrl = data.url;
}
this.$emit('upload-success', data);
},
beforeAvatarUpload(file) {
const isJPG = file.type === 'image/jpeg' || file.type === 'image/png' || file.type === 'image/gif' || file.type === 'image/bmp';
const isLt2M = file.size / 1024 / 1024 < 10;
if (!isJPG) {
this.$message.error('上传图片只能是 JPG、PNG、GIF、BMP 格式!');
return false;
}
if (!isLt2M) {
this.$message.error('上传图片大小不能超过 10MB!');
return false;
}
// 图片尺寸校验
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = (e) => {
const img = new Image();
img.onload = () => {
const { width, height } = img;
let isValid = true;
let errorMessage = '';
// 如果上传尺寸校验的props不存在,使用展示尺寸作为默认值
const effectiveMaxWidth = this.maxWidth !== null ? this.maxWidth : this.width;
const effectiveMaxHeight = this.maxHeight !== null ? this.maxHeight : this.height;
if (width !== effectiveMaxWidth) {
errorMessage = `仅支持上传png/jpg/jpeg文件,上传尺寸为${effectiveMaxWidth}px*${effectiveMaxHeight}px`;
isValid = false;
} else if (height !== effectiveMaxHeight) {
errorMessage = `仅支持上传png/jpg/jpeg文件,上传尺寸为${effectiveMaxWidth}px*${effectiveMaxHeight}px`;
isValid = false;
}
if (!isValid) {
this.$message.error(errorMessage);
reject(new Error(errorMessage));
} else {
resolve(true);
}
};
img.src = e.target.result;
};
reader.readAsDataURL(file);
});
},
// 原生预览,无任何依赖
handlePreview() {
if (!this.imageUrl) return;
this.previewImage = `${this.baseUrl}/file/file/previewByCode/${this.imageUrl}`;
this.previewVisible = true;
},
handleReUpload() {
if (this.readOnly) return;
const uploadInput = this.$el.querySelector('.el-upload__input');
if (uploadInput) {
uploadInput.click();
}
}
}
};
</script>
<style scoped>
.avatar-uploader {
border: none;
border-radius: 6px;
position: relative;
overflow: hidden;
display: flex;
align-items: center;
justify-content: center;
background: #f5f7fa;
}
.avatar-uploader:hover {
cursor: pointer;
}
.image-container .icon-wrapper {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
gap: 20px;
opacity: 0;
transition: opacity 0.3s;
z-index: 10;
}
.image-container:hover .icon-wrapper {
opacity: 1;
}
.image-container .icon-wrapper.readonly {
opacity: 1;
background: transparent;
justify-content: center;
}
.image-container .icon-wrapper.readonly .icon-item {
background: rgba(0, 0, 0, 0.4);
}
.icon-item {
width: 32px;
height: 32px;
border-radius: 50%;
background: rgba(0, 0, 0, 0.6);
display: flex;
align-items: center;
justify-content: center;
color: #fff;
cursor: pointer;
font-size: 16px;
transition: all 0.2s;
}
.icon-item:hover {
background: #409EFF;
transform: scale(1.1);
}
/* 按钮样式 */
.icon-item .el-button {
border: none;
border-radius: 50%;
width: 32px;
height: 32px;
padding: 0;
display: flex;
align-items: center;
justify-content: center;
background: transparent !important;
color: #fff !important;
box-shadow: none !important;
}
.icon-item .el-button:hover {
background: transparent !important;
color: #fff !important;
box-shadow: none !important;
}
.icon-item .el-button:focus {
background: transparent !important;
color: #fff !important;
box-shadow: none !important;
}
.image-container {
position: relative;
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
}
.avatar-uploader img {
width: 100%;
height: 100%;
object-fit: cover;
cursor: default;
}
.avatar-uploader .el-upload {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
}
.avatar-uploader-icon {
font-size: 28px;
color: #8c939d;
text-align: center;
}
.avatar {
display: block;
}
/* 图片预览样式 */
.image-preview-modal {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background: rgba(0, 0, 0, 0.5);
z-index: 9999;
display: flex;
align-items: center;
justify-content: center;
cursor: zoom-out;
}
.preview-image {
max-width: 90%;
max-height: 90%;
cursor: default;
object-fit: contain;
}
</style>
图片:

引用方式:
javascript
<el-form-item prop="douyinQrcode" label="抖音二维码">
<image-upload
v-model="douyinQrcode"
:image-url="douyinQrcode"
:width="90"
:height="90"
:action="baseUrl + '/file/file/upload/banner'"
@upload-success="handleDouyinQrcodeSuccess"
></image-upload>
<p class="hint">仅支持上传png/jpg/jpeg文件,上传尺寸为90*90px,图片大小不超过10M</p>
</el-form-item>