11 changed files with 408 additions and 42 deletions
@ -0,0 +1,207 @@ |
|||
<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> |
Loading…
Reference in new issue