目录
功能说明和具体实现
功能说明:
课程内容可以选择无章节和多章节上传,选择无章节课程则直接上传文件,选择多章节上传时要上传章节名对应的文件,和无章节不同的是,不能一层数据结构直接实现,需要设计两层数据结构,并且都是单文件上传,上传后上传按钮隐藏,移除上传文件后展示上传按钮
选择无章节课程:
选择多章节课程:
无章节课程上传后:
多章节课程上传后:
具体实现:
基本逻辑
通过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)"
两种写法的区别:
- 箭头函数方式:
: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,
......
});