element-plus之文件上传upload组件的fileList特殊用法

4,263 阅读3分钟

一、背景

最近项目中用到图片上传(含预览、删除)功能。于是前端vue3采用饿了么团队的element-plus的upload文件上传组件。按照官网的教程,刚开始都很顺利。多说无益,直接进入重点,项目需求:组卷系统的题目修改功能,包含试题内容、答案、解析、插图的编辑,其中的图片编辑功能功能比较麻烦。 难点:试题原先可能有插图,在修改时,需要删除、插入新图片功能,界面如下 image.png 图1 父组件点击编辑按钮,调用子组件

image.png 图2 子组件上传图片(可多张) 本文主要用到图2,图2的页面是图1点击编辑按钮后,打开图2的对话框(用到dialoge组件从隐藏切换到显示,即visiable=true)

二、踩过的坑

先看下官网的demo示范

image.png 实现关键代码如下(省略部分代码,详情自己看官网):

<template>
  <el-upload
    v-model:file-list="fileList"    
    list-type="picture-card"
    ……
  >
</template>
<script lang="ts" setup>

const fileList = ref<UploadUserFile[]>([
  {
    name: 'food.jpeg',
    url: 'https://fuss10.elemecdn.com/3/63/4e7f3a15429bfda99bce42a18cdd1jpeg.jpeg?imageMogr2/thumbnail/360x360/format/webp/quality/100',
  },
  {
    name: 'plant-1.png',
    url: '/images/plant-1.png',
  },
 ……
])
……
</script>

关于fileList的解释,官网很少。其中发现fileList的内容是写死的。是一个特殊的数组,其元素至少由name和url两部分构成的字典。 刚开始按照官网demo非常顺利实现图片预览、插入、删除功能,因为在测试的时候的case中试题原先没有插图。

卡脖子事件

当遇到试题的插图原先就有图片,则需要先把原先的图片加载进来预览,并作为后面的删除、插入依据。

于是尝试方案1(失败)

我在vue组件的全局变量fileList直接赋值,关键代码如下:

<script setup>
 const props = defineProps({
    type: String,
    father_method : Function,
    question: Object,
  });
  // 把图片images列表中的URL,转换成字典{name,url}
  const convertImage=(images)=>{
    // console.log("待转换的图片列表",images)
    if(images.length>0){
        var pattern=/\d+\.(\w+){3,4}/g          
        let images_url_temp =[]
        for(let item of images){
          var image_name=item.match(pattern).toString()
          images_url_temp.push({name:image_name,url:item})
        }
        return images_url_temp
      }else{
        return Array()
      }      
  }
    let fileList=convertImage(props.question.images)  #关键代码
</script>

其中question是上个页面传递过来的试题内容,从上个页面传过来的images数组只包含图片的url,如http://xx/123.jpg,关键语句是,经过自定义函数convertImage,转换成upload要求的格式。

let fileList=convertImage(props.question.images)

其作用是模仿官网的demo,给fileList赋初始值。

结果报错误, fileList中虽然已经赋值,但是界面中不显示原先的图片(还是没搞清楚原因,知道原因的请在评论区告诉我,非常感谢)

方案2(失败)

在onMouted中加载fileList的值

  onMounted(() => {   
  fileList=convertImage(props.question.images)  
  console.log("初始化:",fileList)
  })

结果还是没成功,忘记错误提示是什么了。 尝试了其他所有的已知能力范围内方案都不行,废了2天时间。(此时我快气到爆炸) 最后去百度上疯狂的学习upload的文章,最后终于在一篇文章中得到启发。(具体内容略过,自己点链接去看) 标题:封装el-upload

t.csdn.cn/lpAHw

方案3(失败)

方法就是把file-list直接绑定到defineProps中传递的值fileList

<template>
<el-upload
   v-model:file-list="fileList"    
   list-type="picture-card"
   ……
 >
</template>
<script setup>
const props = defineProps({
   type: String,
   father_method : Function,
   question: Object,
 });
 ……
</script>

方案4:封装upload组件并浅拷贝(成功了)

先把子组件中的文件上传组件(el-upload)封装起来,再引入该封装的组件,文件名叫UploadImage.vue: 其中关键代码是:

const initFileList=computed(()=>{
   return props.fileList
 })
watch(initFileList,(newVal)=>{
   console.log("uploadimage中监视",newVal)
   fileLists.splice(0,fileLists.length)
   for(let item of newVal){
       fileLists.push(item)
   }
})

大致思路就是把props.fileList的内容浅拷贝给绑定upload组件图片的fileLists 封装文件上传的UploadImage.vue完整代码如下,

<template>
   <div>
       <el-upload
           ref="upload"
           name="images"
           :action="props.action"
           list-type="picture-card"
           accept=".jpg,.jpeg,.png"
           :on-success="handleSuccess"
           :file-list="fileLists"
           :headers="{token:token}"
           :limit="props.limit"
           :on-remove="handleRemove"
           :on-preview="handlePictureCardPreview"
           >
           <el-icon><Plus /></el-icon>
          
       </el-upload>
       <el-dialog v-model="zoomInDialogVisible">
           <img w-full :src="dialogImageUrl" alt="Preview Image" />
       </el-dialog>       
   </div>
</template>

<script setup>
import { localGet } from '@/utils'
import { reactive, ref, watch, computed } from 'vue'
import { Delete, Download, Plus, ZoomIn, EditPen } from '@element-plus/icons-vue'
import { genFileId } from 'element-plus'
const props = defineProps({
   limit:{
       type:Number,
       default:5,
   },
   action:{
       type:String,
       default:'#'
   },
   fileList:{
       type:Array,
       default: ()=>{
           return []
       }
   },
   father_method:Function
});
const upload = ref(null)
const disabled = ref(false)
const emit = defineEmits(['father_method']);
let dialogImageUrl = ref('https://element-plus.org/images/element-plus-logo.svg');
let zoomInDialogVisible = ref(false);
let editURLDialogVisible=ref(false)
let fileLists=ref(props.fileList);
fileLists=props.fileList
const dialogVisible = ref(false)
let editForm=reactive({
   url:'',
   uid:null
})
const editUrlDialogVisible=ref(false)
const token=ref(localGet('token') || '')
const initFileList=computed(()=>{
   return props.fileList
 })
watch(initFileList,(newVal)=>{
   console.log("uploadimage中监视",newVal)
   fileLists.splice(0,fileLists.length)
   for(let item of newVal){
       fileLists.push(item)
   }
})

// 移除图片
const handleRemove=(file,fileList)=> {
   console.log("被删除的文件是:",file)
   console.log("所有文件:",fileList)
   submitFileToFather()
}
const handlePictureCardPreview=(uploadFile)=>{
   console.log("我要放大预览啦", uploadFile.url)
   dialogImageUrl.value = uploadFile.url;
   zoomInDialogVisible.value = true;

}
const handleSuccess=(response,file,fileList)=>{
   // let obj = {
   //     name: file.name,
   //     status: "success",
   //     uid: file.uid,
   //     url: file.url,
   // }
   // fileLists.value.push(obj)
   fileLists=fileList
   submitFileToFather()
}

// 将图片文件传回给父组件
const submitFileToFather=()=>{
   // console.log("我要发送给父组件啦",fileLists)
   if(props.father_method)props.father_method(fileLists)
}
// 用户选择超过规定数量的图片
const handleExceed = (files) => {
   alert("文件超出最大数量")
}
const modifyFileList=(fileList)=>{
   fileLists = fileList
}
 // 将修改fileList的方法暴露出去
defineExpose({ modifyFileList })
</script>

<style scoped>

</style>

子组件调用该封装文件,关键代码如下:

<el-form label-width="100px">
         <el-form-item label="图片:">
            <UploadImage :limit="5" :fileList="state.fileList" :action="state.uploadImgServer" :father_method="getImageList"></UploadImage>
         </el-form-item>
   </el-form>

最后还是有个bug,每次上传、删除图片后,下一次打开其他的试题(图1),编辑图片时,初始状态图片还是上次的编辑后的值。哪位大哥知道原因的请告诉我,非常感谢。 第1次写文章,不知道在哪里上传源代码文件,知道的大哥请告诉我,栓Q