【vue2+Element】多层结构上传文件时遇到的问题和解决方法

功能说明和具体实现

功能说明:

课程内容可以选择无章节和多章节上传,选择无章节课程则直接上传文件,选择多章节上传时要上传章节名对应的文件,和无章节不同的是,不能一层数据结构直接实现,需要设计两层数据结构,并且都是单文件上传,上传后上传按钮隐藏,移除上传文件后展示上传按钮

选择无章节课程:
无章节课程
选择多章节课程:
在这里插入图片描述
无章节课程上传后:
在这里插入图片描述
多章节课程上传后:
在这里插入图片描述

具体实现:

基本逻辑

通过v-if判断是无章节还是多章节,多章节时,数据结构如下:

 ruleForm: {
   .......
   multipleChapters: [
     { chapterName: '', coursewareType: '', coursewarePath: '', coursewareName: '', fileList: [] },
   ],

通过v-for遍历ruleForm的multipleChapters, 作为响应式数据绑定到DOM,初始化每个章节的章节名称和课程文件,添加和删除某个章节也是直接对ruleForm的multipleChapters进行操作

  <el-form-item label="课程内容:" prop="multipleChapter">
	  <el-radio v-model="ruleForm.multipleChapter" label="0">无章节课程</el-radio>
	   <el-radio v-model="ruleForm.multipleChapter" label="1">多章节课程</el-radio>
	   <div class="course-form-item">
	     <span style="font-weight: 600; margin-top: 10px">提示:</span>
	     <span>本地上传音视频最大支持3GB(文档最大支持200M),mp4、mp3、pdf格式文件</span>
	     <div v-if="ruleForm.multipleChapter == 1" class="multiple-button" >
	       <el-button style="margin: 20px 0 10px 0" round icon="el-icon-plus" @click="addNewSection">
	         添加章节
	       </el-button>
	       <div class="course-form-item-wrap" v-for="(item, idx) in ruleForm.multipleChapters" :key="idx">
	         <div class="course-form-item-name">
	           <el-input v-model="item.chapterName" placeholder="请输入章节名称"></el-input>
	           <i class="el-icon-delete" @click="delOneSection(idx)"></i>
	         </div>
	         <div class="course-form-item-upload">
	           <el-upload
	             class="avatar-uploader"
	             :file-list="ruleForm.multipleChapters[idx].fileList"
	             :on-remove="(file, fileList) => handleRemoveMultiple(file, fileList, idx)"
	             :show-file-list="true"
	             :multiple="false"
	             limit="1"
	             action="/upload"
	             :http-request="e => handleAvatarUploadDemo(e, 'cover2', idx)"
	             :before-upload="beforeVideoUpload"
	             :accept="accept">
	             <div v-if="item.fileList.length === 0">
	               <i style="color: #2a83cb" class="el-icon-upload2"></i>
	               <span style="color: #2a83cb">从本地上传</span>
	             </div>
	           </el-upload>
	         </div>
	       </div>
	     </div>
	     <div v-else>
	       <el-upload
	         class="course-form-item-single"
	         :file-list="coursewarePath1"
	         :on-remove="handleRemoveSingle"
	         :show-file-list="true"
	         :multiple="false"
	         limit="1"
	         action="/upload"
	         :http-request="e => handleAvatarUploadDemo(e, 'cover1')"
	         :before-upload="beforeVideoUpload"
	         :accept="accept">
	         <el-button
	           v-if="ruleForm.coursewarePath == ''"
	           style="margin: 20px 0 10px 0; display: block"
	           round
	           icon="el-icon-upload2">
	           本地上传
	         </el-button>
	       </el-upload>
	     </div>
	   </div>
 </el-form-item>

问题一:文件上传组件的钩子没有正确添加v-for的idx导致死循环

问题描述:
:on-remove="(file, fileList) => handleRemoveMultiple(file, fileList, idx)"

在移除文件时,需要用到v-for的idx,来操作具体移除具体哪个章节的课程文件,如果写成:on-remove=“handleRemoveMultiple(file, fileList, idx)”,handleRemoveMultiple中不能成功获取参数中的idx,多章节课程的radio无法点击,控制台打印显示出现死循环,只能在浏览器中关闭tab重新进入项目才能恢复正常
在这里插入图片描述

   :http-request="e => handleAvatarUploadDemo(e, 'cover2', idx)"

也是同样的情况

原因和解决方法:

查阅elementui官网:
在这里插入图片描述
移除文件的钩子默认有两个参数,如果要添加新的参数,要这样写,handleRemoveMultiple函数才能获取到idx的值,否则idx 为 undefined,可能会导致删除操作无法正确执行,从而进入死循环。

:on-remove="(file, fileList) => handleRemoveMultiple(file, fileList, idx)"

两种写法的区别:

  1. 箭头函数方式:
:on-remove="(file, fileList) => handleRemoveMultiple(file, fileList, idx)"

这种方式使用了箭头函数,箭头函数会绑定外层作用域的 this,因此在 handleRemoveMultiple 函数中可以访问到外部的 idx 变量

2.直接调用函数方式:

:on-remove="handleRemoveMultiple(file, fileList, idx)"

这种方式直接调用了 handleRemoveMultiple 函数,但是由于没有绑定外部作用域,handleRemoveMultiple 函数中无法访问到外部的 idx 变量,因此会导致 idx 为 undefined。

问题二:成功获取index后无法响应式修改文件列表数据

问题描述

以移除操作为例:

 handleRemoveMultiple(file, fileList, idx) {
   console.log('object', file, fileList, idx);
    this.$set(this.ruleForm.multipleChapters, idx, {
      chapterName: this.ruleForm.multipleChapters[idx].chapterName,
      coursewarePath: '',
      coursewareType: '',
      coursewareName: '',
      fileList: [],
    });
    // this.$set(this.ruleForm.multipleChapters[idx], 'fileList', []);
    // const index = this.ruleForm.multipleChapters.findIndex(item => item.coursewarePath === file.url);
},

修改数据时,如果直接这样修改:

  this.ruleForm.multipleChapters[idx].fileList = [];

控制台打印发现,数据是可以成功修改,但是DOM不能根据数据的变化及时更新,因为移除文件后需要重新展示上传按钮,这种情况下不能成功展示。

原因和解决方法

因为此时不是响应式修改数据,Vue 无法自动追踪到属性的变化从而更新页面展示,所以要响应式修改数据:

 handleRemoveMultiple(file, fileList, idx) {
      console.log('object', file, fileList, idx);
      this.$set(this.ruleForm.multipleChapters, idx, {
        chapterName: this.ruleForm.multipleChapters[idx].chapterName,
        coursewarePath: '',
        coursewareType: '',
        coursewareName: '',
        fileList: [],
      });
 }

这样数据不仅成功修改,页面也能成功更新

注意:
如果这样写:

 handleRemoveMultiple(file, fileList, idx) {
      console.log('object', file, fileList, idx);
      this.$set(this.ruleForm.multipleChapters, idx, {      
        coursewarePath: '',
        coursewareType: '',
        coursewareName: '',
        fileList: [],
      });
 }

章节的课程文件可以成功响应式修改了,但是章节对应的课程名称却会重置,因为 this.$set()对数组操作时,是重新替换掉数组某一项的全部数据,不会保留没有变化的数据,所以在操作时,要对章节名称也进行赋值,保证该字段不会被重置:

 this.$set(this.ruleForm.multipleChapters, idx, {
  chapterName: this.ruleForm.multipleChapters[idx].chapterName,
  ......
  });
  • 19
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值