AI介绍:
- 显示已上传的图片列表,并提供删除和预览功能。
- 支持上传新的图片,并在上传成功后显示在列表中。
代码的具体步骤如下:
- 在模板中,使用
v-for
指令遍历imageUrls
数组,显示已上传的图片,并为每张图片绑定鼠标移入和移出事件。 - 使用
el-upload
组件实现图片上传功能,配置了上传成功、上传前、上传进度等事件的处理函数。 - 使用
el-dialog
组件实现图片预览功能,点击图片时弹出对话框显示大图。 - 在脚本部分,使用
ref
和watch
来监听图片列表的变化,并处理上传成功、上传前、预览、删除等操作的逻辑。 - 使用
showIcons
函数控制鼠标移入移出时的图标显示和隐藏。
整体来说,该代码实现了一个功能完善的图片上传组件,用户可以上传、预览和删除图片。
父组件使用:
双向绑定后数据会更新
<UploadImg v-model:img-list="formData.afterRepairImg"></UploadImg>
子组件即封装
<template>
<div class="img-box">
<div class="image-container" v-for="(item, i) in imageUrls" :key="item">
<img class="image" :src="item" @mouseover="showIcons" @mouseleave="hideIcons" />
<div class="icon-container" v-show="showIcons">
<el-icon class="zoom-icon" @click="handlePictureCardPreview(item)"><zoom-in /></el-icon>
<el-icon class="delete-icon" @click="handleRemove(i)"><Delete /></el-icon>
</div>
</div>
<el-upload
class="avatar-uploader"
action="#"
list-type="picture-card"
:http-request="customUpload"
:on-success="handleAvatarSuccess"
:before-upload="beforeAvatarUpload"
:on-preview="handlePictureCardPreview"
:file-list="fileList"
:show-file-list="false"
multiple
>
<el-icon class="avatar-uploader-icon">
<Plus />
</el-icon>
</el-upload>
</div>
<el-dialog v-model="dialogVisible" width="90%" top="20px">
<img w-full :src="dialogImageUrl" alt="Preview Image" />
</el-dialog>
</template>
<script lang="ts" setup>
import { ref, watch } from 'vue'
import { UploadProps, ElMessage, UploadUserFile } from 'element-plus'
import { photoUploadFileAPI } from '@/api/upload'
import { Delete, Download, Files, Plus, ZoomIn } from '@element-plus/icons-vue'
const props = defineProps({
imgList: {
type: Array,
required: true
}
})
const imageUrl = ref('')
const imageUrls = ref([])
const fileList = ref<UploadUserFile[]>([])
const emit = defineEmits(['update:imgList'])
// 监听 imgList 的变化
watch(
() => props.imgList,
(newVal, oldVal) => {
if (newVal.length > 0) {
// console.log('父组件图片数组', props.imgList, fileList)
imageUrls.value = newVal as any
}
// 在这里可以执行其他逻辑
}
)
const handleAvatarSuccess: UploadProps['onSuccess'] = (response, uploadFile) => {
// console.log('你从', response, uploadFile)
imageUrl.value = URL.createObjectURL(uploadFile.raw!)
}
// 上传文件,请求接口,并且将结果存储
function customUpload(file: any) {
let formData = new FormData()
// console.log('查看', file.file)
formData.append('file', file.file)
photoUploadFileAPI(formData).then((res: any) => {
const imgUrl = res.data.data
imageUrls.value.push(imgUrl)
console.log('图片提交返回', res, imageUrls, fileList)
emit('update:imgList', imageUrls)
})
}
const beforeAvatarUpload: UploadProps['beforeUpload'] = rawFile => {
const isImage = rawFile.type.startsWith('image/') // 检查是否为图片类型
if (!isImage) {
ElMessage.error('只能上传图片文件!')
return false
}
// if (rawFile.type !== 'image/jpeg') {
// ElMessage.error('Avatar picture must be JPG format!')
// return false
// } else if (rawFile.size / 1024 / 1024 > 2) {
// ElMessage.error('Avatar picture size can not exceed 2MB!')
// return false
// }
return true
}
const handleUploadProgress: UploadProps['onProgress'] = (event, file, fileList) => {
// 处理上传进度的逻辑,例如更新进度条状态
const uploadFile = fileList.find(item => item.uid === file.uid)
if (uploadFile) {
uploadFile.percentage = event.percent // 更新上传进度
fileList.value = [...fileList] // 更新文件列表
}
const fileItem = fileList.value.files.find((f: any) => f.file === file);
if (fileItem) {
fileItem.progress = Math.round(event.percent);
}
}
const dialogImageUrl = ref('')
const dialogVisible = ref(false)
const disabled = ref(false)
const handlePictureCardPreview: UploadProps['onPreview'] = uploadFile => {
dialogImageUrl.value = uploadFile as any
dialogVisible.value = true
}
const handleRemove = (i: number) => {
// 从 fileList 中移除被删除的文件
// fileList.value = fileList.value.filter(file => file.uid !== uploadFile.uid)
imageUrls.value.splice(i, 1)
console.log(i, imageUrls)
emit('update:imgList', imageUrls)
}
const showControlsIcons = ref(false)
const showIcons = () => {
// console.log('鼠标移入')
showControlsIcons.value = true
}
const hideIcons = () => {
// console.log('鼠标移出')
showControlsIcons.value = false
}
</script>
<style lang="scss" scoped>
.img-box {
width: 100%;
display: flex;
flex-wrap: wrap;
.image-container {
position: relative;
display: inline-block;
cursor: pointer;
img {
border: 1px dashed var(--el-border-color);
font-size: 28px;
color: #8c939d;
width: 150px;
height: 150px;
text-align: center;
border-radius: 10px;
object-fit: cover;
margin: 0 1px;
}
}
}
.icon-container {
position: absolute;
top: 0;
left: 0;
// top: 50%;
// left: 50%;
// transform: translate(-50%, -50%);
display: flex;
align-items: center;
justify-content: center;
background-color: rgba(0, 0, 0, 0.5);
color: #fff;
width: 150px;
height: 150px;
border-radius: 10px;
opacity: 0;
transition: opacity 0.3s;
.el-icon {
font-size: 20px;
margin: 0 10px;
}
}
.image-container:hover .icon-container {
opacity: 1;
}
.avatar-uploader .avatar {
width: 178px;
height: 178px;
display: block;
}
</style>
<style lang="scss">
.avatar-uploader .el-upload {
border: 1px dashed var(--el-border-color);
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
transition: var(--el-transition-duration-fast);
}
.avatar-uploader .el-upload:hover {
border-color: var(--el-color-primary);
}
.el-icon.avatar-uploader-icon {
font-size: 28px;
color: #8c939d;
width: 178px;
height: 178px;
text-align: center;
}
.el-upload-list__item {
align-items: center;
}
.el-dialog__body {
margin-top: 20px;
text-align: center;
img {
min-height: 50vh;
}
}
</style>