You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

208 lines
5.5 KiB

<template>
<div class="upload-wrapper">
<!-- 预览图片 -->
<div class="image-list" v-if="previewList.length>0">
<div class="image-box"
v-for="(item, index) in previewList" :key="index">
<img :src="item.url" class="image-preview" />
<!-- <i class="el-icon-close delete-icon" @click.stop="removeImage(index)" />-->
</div>
</div>
<!-- 上传按钮 -->
<el-upload
class="image-uploader"
action=""
:http-request="uploadHandler"
:show-file-list="false"
:before-upload="beforeUpload"
:accept="acceptMime"
>
<GuipButton class="upload-button" type="ignore" :btnstyle="btnStyle">
<div class="bgImg"></div>
<span>选择图片</span>
</GuipButton>
</el-upload>
</div>
</template>
<script>
import GuipButton from "@/components/GuipButton.vue";
export default {
name: "ImgUpload",
components: {GuipButton},
props: {
value: {
type: [Array, File, null],
default: () => []
},
defaultUrls: {
type: Array,
default: () => []
},
acceptTypes: {
type: Array,
default: () => ['jpg', 'png']
},
sizeLimit: {
type: Number,
default: 2 // MB
},
btnStyle:{
type: Object,
default() {
return {
width: '118px',
color: '#23242B',
border: '1px solid #BABDC2',
background: '#F2F3F5',
'border-radius': '4px'
};
}
},
maxCount: {
type: Number,
default: 1
}
},
data() {
return {
previewList: [],
initialized: false
};
},
watch: {
defaultUrls(newUrls) {
if (!this.initialized && Array.isArray(newUrls) && newUrls.length) {
this.previewList = newUrls
.filter(url => !!url)
.map(url => ({ url }));
this.initialized = true;
}
}
},
computed: {
// 根据后缀数组生成 MIME 类型字符串,用于 <el-upload accept="">
acceptMime() {
const map = {
jpg: 'image/jpeg',
jpeg: 'image/jpeg',
png: 'image/png',
webp: 'image/webp',
gif: 'image/gif'
};
return this.acceptTypes
.map(ext => map[ext.toLowerCase()])
.filter(Boolean)
.join(',');
}
},
methods: {
beforeUpload(file) {
if(this.maxCount === 1){
this.previewList = []
}
if (this.previewList.length >= this.maxCount) {
this.$message.warning(`最多只能上传 ${this.maxCount} 张图片`);
return false;
}
const isImage = file.type.startsWith("image/");
const isLt2M = file.size / 1024 / 1024 < 2;
if (!isImage) {
this.$message.error("只能上传图片格式!");
return false;
}
if (!isLt2M) {
this.$message.error("图片大小不能超过 2MB!");
return false;
}
const allowedTypes = this.acceptMime.split(',');
if (!allowedTypes.includes(file.type)) {
this.$message.error(`只允许上传 ${this.acceptTypes.join('/').toUpperCase()} 格式图片!`);
return false;
}
const url = URL.createObjectURL(file);
if(this.maxCount === 1){
this.previewList = [{ file, url }];
this.$emit('input', { file, url });
} else {
this.previewList.push({ file, url });
const files = this.previewList
.filter(item => item.file)
.map(item => item.file);
this.$emit('input', files);
}
return false;
},
uploadHandler() {
// 留空,使用自定义上传逻辑,由父组件决定是否提交 file 到服务器
},
removeImage(index) {
this.previewList.splice(index, 1);
const files = this.previewList
.filter(item => item.file)
.map(item => item.file);
this.$emit('input', files);
}
}
};
</script>
<style scoped>
.upload-wrapper {
}
.image-list {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin-bottom: 10px;
}
.image-box {
position: relative;
width: 100px;
height: 100px;
}
.image-preview {
width: 100%;
height: 100%;
object-fit: cover;
border: 1px solid #ccc;
border-radius: 4px;
}
.delete-icon {
position: absolute;
top: -6px;
right: -6px;
font-size: 16px;
color: white;
background: #f56c6c;
border-radius: 50%;
padding: 2px;
cursor: pointer;
}
.bgImg{
width: 20px;
height: 16px;
margin-right: 6px;
background-image: url(@/assets/site/uploadIcon.svg);
}
.upload-button:hover{
border: 1px solid #006AFF!important;
}
.upload-button:hover .bgImg {
background-image: url(@/assets/site/uploadIcon_light.svg);
}
.upload-button:hover span {
color: #006AFF;
}
</style>