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