记录————封装vue3+element-plus(el-upload)上传图片组件

AI介绍:

  1. 显示已上传的图片列表,并提供删除和预览功能。
  2. 支持上传新的图片,并在上传成功后显示在列表中。

代码的具体步骤如下:

  • 在模板中,使用v-for指令遍历imageUrls数组,显示已上传的图片,并为每张图片绑定鼠标移入和移出事件。
  • 使用el-upload组件实现图片上传功能,配置了上传成功、上传前、上传进度等事件的处理函数。
  • 使用el-dialog组件实现图片预览功能,点击图片时弹出对话框显示大图。
  • 在脚本部分,使用refwatch来监听图片列表的变化,并处理上传成功、上传前、预览、删除等操作的逻辑。
  • 使用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>
相关推荐
生椰拿铁You几秒前
前端前置——ajax
前端·javascript·ajax
海天鹰1 分钟前
JS保留两位小数
javascript
顾辰呀23 分钟前
实现uniapp-微信小程序 搜索框+上拉加载+下拉刷新
前端·windows
hjt_未来可期31 分钟前
vue输入中文,获取英文首字母缩写
javascript·vue.js·ecmascript
计算机学姐34 分钟前
基于Python的影院电影购票系统
开发语言·vue.js·后端·python·mysql·pycharm·pip
前端Hardy35 分钟前
HTML&CSS:爱上班的猫咪
前端·javascript·css·vue.js·html
EricWang135835 分钟前
[OS] 项目3入手
java·linux·前端
烟雨666_java44 分钟前
csrf令牌
android·前端·csrf
2401_858120531 小时前
Spring Boot框架下的教育导师匹配系统
java·前端·spring boot