本篇文章是《使用 element 的 upload 组件实现一个完整的文件上传功能 (上)》的续篇.
话不多说, 接着上一篇直接开始
一. 功能完善 - 保存表格中每一列的文件列表状态
1. 思路
保存表格中每一列的文件列表状态, 这个功能是什么意思呢, 我们先看下前面示例的效果.
在上面这个操作中, 我们做了两件事:
1. 给表格第一列的上传了一个附件图片
2. 点击表格第二列, 第三列, 第四列的上传按钮, 分别查看这三列的附件列表
那么最后的结果发现后三列的附件列表展示的都是第一列的附件图片, 这个显然不符合正常的逻辑. 仔细去看看我们的代码并且思考一下, 也很快能知道这个问题出现的原因: 我们给 < el-upload > 的 file-list 属性绑定了 attachList 数据. attachList 这个值初始是空数组, 当我们点击第一列的附件管理上传一张图片后, attachList 数组就会增加一个元素. 而所有上传按钮触发打开的弹框组件是同一个 (我们页面中只有一个 < el-upload > 元素), 而弹框组件绑定的文件列表数据 attachList 也是公用的, 因此就会出现上面的情况. 到这里也很容易能想到思路去解决这个问题: 不同弹窗绑定的文件列表数据 attachList 分开保存.
那这个办法的言外之意就是需要在 data 中定义 4 个 attachList, 那定义四个数据, 我们就得定义写四个 < el-dialog > 分别去绑定这个四个数据.
这个办法到是能解决问题, 但是假如我们的表格有 100 行数据呢, 我们难道要定义 100 个 attachList, 在写 100 个 < el-dialog > 吗? 这显然就不现实了.
然后我换了个思路: 定义一个数组去保存不同的文件列表数据. 这样在每次点击上传按钮时, 将该列的文件列表数据赋值给另外一个数据 currentAttachList, 然后我们的 < el-dialog > 组件只需要绑定这个 currentAttachList 数据即可. 这样就省事多了.
最后就是保存不同的文件列表数据的数组, 这个要怎么定义呢. 实际上也很简单, 我们可以将这个数据当做表格数据的一个属性定义在 tableData 中.
- currentAttachList: [],
- tableData: [{
- date: '2016-05-02',
- name: '王小虎',
- address: '上海市普陀区金沙江路',
- attachList:[]
- }, {
- date: '2016-05-04',
- name: '王小虎',
- address: '上海市普陀区金沙江路',
- attachList:[]
- }, {
- date: '2016-05-01',
- name: '王小虎',
- address: '上海市普陀区金沙江路',
- attachList:[]
- }, {
- date: '2016-05-03',
- name: '王小虎',
- address: '上海市普陀区金沙江路',
- attachList:[]
- }]
tableData 中的 attachList 就是我们定义的文件列表数据.
数据定义好了之后, 我们继续下面的工作.
2. 上传按钮的点击事件修改
根据前面我们写的一大堆的思路, 可以知晓当我们点击 [附件管理] 按钮时, 需要做两件事:
1. 获取这一列表格数据中的附件列表, 赋值给 currentAttachList
2. 将控制弹框显示的 dialogVisible 设置为 true, 让弹框显示
那我们之前写的点击 [附件管理] 按钮的事件处理程序如下:
<el-button size='small' type="primary" @click="dialogVisible = true">
上传
- <i class="el-icon-upload el-icon--right">
- </i>
- </el-button>
所以现在我们需要将点击事件改为函数调用.
在这之前呢, 我们说了需要获取上传按钮对应那一列的 attachList 数据, 那我们如何知道当前点击的上传按钮是属于表格的第几列呢? 这个我们使用插槽就可以实现了.
- <el-table-column
- prop="attach"
- label="附件管理"
- width="180">
- <template slot-scope="scope">
- <!-- 上传按钮绑定 click 事件 -->
- <el-button size='small' type="primary" @click="uploadBtnClick(scope.$index)">
上传
- <i class="el-icon-upload el-icon--right"></i>
- </el-button>
- </template>
- </el-table-column>
uploadBtnClick 函数实现:
- uploadBtnClick (index){
- // 获取上传按钮对应那一列表格数据中的附件列表, 赋值给 currentAttachList
- this.currentAttachList = this.tableData[index].attachList;
- // 将控制弹框显示的 dialogVisible 设置为 true, 让弹框显示
- this.dialogVisible = true;
- }
这两件事情完成后呢, 记得将 < el-dialog > 的 file-list 绑定的数据改为 currentAttachList.
最后完整的 App.vue 组件代码如下
- <template>
- <div id="app">
- <!-- element-ui Table 表格组件 -->
- <el-table
- class="my-table"
- :data="tableData"
- stripe
- style="width:725px;">
- <el-table-column
- prop="date"
- label="日期"
- width="180">
- </el-table-column>
- <el-table-column
- prop="name"
- label="姓名"
- width="180">
- </el-table-column>
- <el-table-column
- prop="address"
- label="地址"
- width="180">
- </el-table-column>
- <!-- 添加一列附件管理 (文件上传) -->
- <el-table-column
- prop="attach"
- label="附件管理"
- width="180">
- <template slot-scope="scope">
- <!-- 上传按钮绑定 click 事件 -->
- <el-button size='small' type="primary" @click="uploadBtnClick(scope.$index)">
上传
- <i class="el-icon-upload el-icon--right"></i>
- </el-button>
- </template>
- </el-table-column>
- </el-table>
- <el-dialog
- title="附件管理"
- :visible.sync="dialogVisible"
- width="30%">
- <!-- 将 < el-upload > 代码添加到 < el-dialog > 代码块中 -->
- <el-upload
- class="upload-demo"
- drag
- action="https://jsonplaceholder.typicode.com/posts/"
- :file-list="currentAttachList">
- <i class="el-icon-upload"></i>
- <div class="el-upload__text"> 将文件拖到此处, 或 < em > 点击上传 </em></div>
- <div class="el-upload__tip" slot="tip"> 只能上传 jpg/PNG 文件, 且不超过 500kb</div>
- </el-upload>
- <span slot="footer" class="dialog-footer">
- <el-button @click="dialogVisible = false"> 取 消 </el-button>
- <el-button type="primary" @click="dialogVisible = false"> 确 定 </el-button>
- </span>
- </el-dialog>
- </div>
- </template>
- <script>
- export default {
- name: 'App',
- data () {
- return {
- // 添加属性, 默认值为 false, 表示弹框不显示
- dialogVisible: false,
- // 设置当前文件列表数据 currentAttachList, 每次用户点击上传按钮, 该数据就会被赋值为当前按钮那一列 tableData 中的 attachList 数据
- currentAttachList: [],
- tableData: [{
- date: '2016-05-02',
- name: '王小虎',
- address: '上海市普陀区金沙江路',
- attachList:[]
- }, {
- date: '2016-05-04',
- name: '王小虎',
- address: '上海市普陀区金沙江路',
- attachList:[]
- }, {
- date: '2016-05-01',
- name: '王小虎',
- address: '上海市普陀区金沙江路',
- attachList:[]
- }, {
- date: '2016-05-03',
- name: '王小虎',
- address: '上海市普陀区金沙江路',
- attachList:[]
- }]
- }
- },
- methods: {
- uploadBtnClick (index){
- // 获取上传按钮对应那一列表格数据中的附件列表, 赋值给 currentAttachList
- this.currentAttachList = this.tableData[index].attachList;
- // 将控制弹框显示的 dialogVisible 设置为 true, 让弹框显示
- this.dialogVisible = true;
- }
- }
- }
- </script>
- <style>
- #App {
- font-family: 'Avenir', Helvetica, Arial, sans-serif;
- -webkit-font-smoothing: antialiased;
- -moz-OS X-font-smoothing: grayscale;
- color: #2c3e50;
- margin: 50px 30px;
- text-align: center;
- }
- #App .el-dialog__header{
- background:#EBEEF5;
- border-bottom: 1px solid#EBEEF5;
- }
- #App .el-dialog{
- text-align: left;
- }
- #App .el-upload,#App .el-upload .el-upload-dragger{
- width: 100%;
- }
- </style>
- <style scoped>
- #App .my-table{
- display: inline-block;
- border: 1px solid #EBEEF5;
- }
- </style>
在浏览器中看下效果:
仔细的看几遍这个效果, 描述一下我们的操作和结果:
1. 在第四列上传了一张图片, 完成之后关闭弹窗
2. 点击第三列的上传按钮, 获取 tableData 中第三列的 attachList 赋值给 currentAttachList, 此时第三列的 attachList 为空, 所以 currentAttachList 也是空, 所以第三列的附件列表展示为空, 正常.(可以看到第三列的弹框点开后文件列表是一个从无到有的动画, 是因为第四列上传了一个图片, currentAttachList 包含一个元素, 当点击第三列的上传按钮时将 currentAttachList 赋值为空, 而 element-ui 提供的控件是包含动画的, 所以就有了这个视觉上不太好的效果)
然而, 我们最后还有一个操作: 在查看完第三列的文件列表后, 在返回点击第四列的附件管理按钮, 查看第一个操作上传的文件列表. 最后这个操作, 我们惊奇的发现前面上传在第四列的文件列表丢了.
这个问题也比较好理解: 上传完成后, 需要将上传成功的文件信息保存到对应的那一列的 attachList 数组中. 前面写的代码, 我们只读取了 tableData 中的 attachList, 在上传成功以后却没有将文件的信息保存到 attachList 里面, 那么每次重新点击 [附件管理] 按钮, 从 tableData 获取的 attachList 永远是空, 在赋值给 currentAttachList, 文件列表就什么也不会展示. 现在我们可以接着修改代码了, 需要修改的内容如下:
1.<el-upload > 添加 on-success 钩子函数, 当上传成功将本次上传的文件信息 push 到对应 tableData.attachList
2. 添加 methods:uploadSuccess
关于这个 uploadSuccess 函数, 它需要将上传成功的文件信息保存到对应的 tableData.attachList, 那我们就需要知道当前是一列的按钮触发的弹框. 这个问题就是之前在 uploadBtnClick 函数传递的参数 index, 所以我们需要将这个 index 保存到 vue 的数据属性上, 这样在 uploadSuccess 函数中也能用上.
- data () {
- return {
- // 当前点击打开弹框的按钮在表格中是那一列
- currentIndex: 0,
- }
- }
uploadBtnclick 方法需要新增加下面的代码
- uploadBtnClick (index){
- // 设置 currentIndex
- this.currentIndex = index;
- },
uploadSuccess 实现
- uploadSuccess(response, file, fileList){
- var currentIndex = this.currentIndex;
- this.tableData[currentIndex].attachList.push({
- 'name':file.name
- });
- }
最终完整的 App.vue 代码如下
- <template>
- <div id="app">
- <!-- element-ui Table 表格组件 -->
- <el-table
- class="my-table"
- :data="tableData"
- stripe
- style="width:725px;">
- <el-table-column
- prop="date"
- label="日期"
- width="180">
- </el-table-column>
- <el-table-column
- prop="name"
- label="姓名"
- width="180">
- </el-table-column>
- <el-table-column
- prop="address"
- label="地址"
- width="180">
- </el-table-column>
- <!-- 添加一列附件管理 (文件上传) -->
- <el-table-column
- prop="attach"
- label="附件管理"
- width="180">
- <template slot-scope="scope">
- <!-- 上传按钮绑定 click 事件 -->
- <el-button size='small' type="primary" @click="uploadBtnClick(scope.$index)">
上传
- <i class="el-icon-upload el-icon--right"></i>
- </el-button>
- </template>
- </el-table-column>
- </el-table>
- <el-dialog
- title="附件管理"
- :visible.sync="dialogVisible"
- width="30%">
- <!-- 将 < el-upload > 代码添加到 < el-dialog > 代码块中 -->
- <el-upload
- class="upload-demo"
- drag
- action="https://jsonplaceholder.typicode.com/posts/"
- :file-list="currentAttachList"
- :on-success="uploadSuccess">
- <i class="el-icon-upload"></i>
- <div class="el-upload__text"> 将文件拖到此处, 或 < em > 点击上传 </em></div>
- <div class="el-upload__tip" slot="tip"> 只能上传 jpg/PNG 文件, 且不超过 500kb</div>
- </el-upload>
- <span slot="footer" class="dialog-footer">
- <el-button @click="dialogVisible = false"> 取 消 </el-button>
- <el-button type="primary" @click="dialogVisible = false"> 确 定 </el-button>
- </span>
- </el-dialog>
- </div>
- </template>
- <script>
- export default {
- name: 'App',
- data () {
- return {
- // 添加属性, 默认值为 false, 表示弹框不显示
- dialogVisible: false,
- // 设置当前文件列表数据 currentAttachList, 每次用户点击上传按钮, 该数据就会被赋值为当前按钮那一列 tableData 中的 attachList 数据
- currentAttachList: [],
- // 当前点击打开弹框的按钮是那一列
- currentIndex: 0,
- tableData: [{
- date: '2016-05-02',
- name: '王小虎',
- address: '上海市普陀区金沙江路',
- attachList:[]
- }, {
- date: '2016-05-04',
- name: '王小虎',
- address: '上海市普陀区金沙江路',
- attachList:[]
- }, {
- date: '2016-05-01',
- name: '王小虎',
- address: '上海市普陀区金沙江路',
- attachList:[]
- }, {
- date: '2016-05-03',
- name: '王小虎',
- address: '上海市普陀区金沙江路',
- attachList:[]
- }]
- }
- },
- methods: {
- uploadBtnClick (index){
- // 获取上传按钮对应那一列表格数据中的附件列表, 赋值给 currentAttachList
- this.currentAttachList = this.tableData[index].attachList;
- // 将控制弹框显示的 dialogVisible 设置为 true, 让弹框显示
- this.dialogVisible = true;
- // 设置 currentIndex
- this.currentIndex = index;
- },
- uploadSuccess(response, file, fileList){
- var currentIndex = this.currentIndex;
- this.tableData[currentIndex].attachList.push({
- 'name':file.name
- });
- }
- }
- }
- </script>
- <style>
- #App {
- font-family: 'Avenir', Helvetica, Arial, sans-serif;
- -webkit-font-smoothing: antialiased;
- -moz-OS X-font-smoothing: grayscale;
- color: #2c3e50;
- margin: 50px 30px;
- text-align: center;
- }
- #App .el-dialog__header{
- background:#EBEEF5;
- border-bottom: 1px solid#EBEEF5;
- }
- #App .el-dialog{
- text-align: left;
- }
- #App .el-upload,#App .el-upload .el-upload-dragger{
- width: 100%;
- }
- </style>
- <style scoped>
- #App .my-table{
- display: inline-block;
- border: 1px solid #EBEEF5;
- }
- </style>
浏览器查看结果:
可以发现, 前面的问题已经被我们成功解决.
3. 动画删除
现在呢, 这个功能已经实现了. 唯一不好的视觉效果就是文件列表的那个动画, 我们将这个动画删除.
- <style>
- #App .el-upload-list li{
- transition: none;
- }
动画删除后的效果
可以看到, 现在的效果就正常了.
三. 功能完善 - 删除附件
删除文件这个功能,<el-upload > 组件本身是支持的, 只是在我们这种多文件上传的情况中, 还需要添加一下代码.
我们先看一下组件本身提供的这个删除功能
我们的操作顺序和结果如下
1点击第四列上传按钮
2成功上传两张图片
3删除第二张图片
4关闭弹窗, 查看第四列文件列表, 文件列表显示正常
5点击第三列上传按钮, 文件列表为空显示正常
6在点击第四列的上传按钮, 发现前面删除的那张图片依然显示在文件列表中.
关于我们删除第四列的一张图片后, 在第6步点击查看发现图片依然存在的这个问题很好解释, 我们可以回头看一下 uploadBtnclick 函数的逻辑:
每次点击上传按钮, 将对应的 tableData.attachList 赋值给 currentAttachList.
而我们删除的时候, 并没有删除对应的 tableData.attachList 中的数据, 所以给 currentAttachList 的赋值操作导致文件列表展示的依然是之前的数据.
但是这里有一个疑惑的点就是第4个步骤: 关闭弹窗, 查看第四列文件列表, 文件列表显示正常. 这个就比较奇怪了, 删除第二张图片后, 按照前面我们梳理的 uploadBtnclick 函数的逻辑, 此时文件列表应该还是会包含删除的那个文件.
关于这个问题, 我们在 uploadBtnClick 函数中添加一些打印信息:
- uploadBtnClick (index){
- console.log('uploadBtnClick');
- console.log("this.tableData[index].attachList");
- console.log(this.tableData[index].attachList);
- // 获取上传按钮对应那一列表格数据中的附件列表, 赋值给 currentAttachList
- this.currentAttachList = this.tableData[index].attachList;
- console.log("this.currentAttachList");
- console.log(this.currentAttachList);
- // 将控制弹框显示的 dialogVisible 设置为 true, 让弹框显示
- this.dialogVisible = true;
- // 设置 currentIndex
- this.currentIndex = index;
- },
然后截图看一下步骤4中的打印信息:
可以看到,<el-upload > 的 file-list 绑定的 currentAttachList 是包含两个元素 (其中包含第3步删除的那个), 但是文件列表却只显示了一个.
enmmmmmm, 这个地方比较费解.
我们先处理第六个步骤中删除出现的异常显示. 根据我们前面的梳理的逻辑, 需要做两个修改
methods 添加 handleRemove 函数处理删除数据的功能
<el-upload > 添加 on-remove 钩子函数调用 handleRemove
1.<el-upload > 添加 on-remove 钩子函数调用 handleRemove
- <el-upload
- class="upload-demo"
- drag
- action="https://jsonplaceholder.typicode.com/posts/"
- :on-remove="handleRemove"
- :file-list="currentAttachList"
- :on-success="uploadSuccess">
- <i class="el-icon-upload"></i>
- <div class="el-upload__text"> 将文件拖到此处, 或 < em > 点击上传 </em></div>
- <div class="el-upload__tip" slot="tip"> 只能上传 jpg/PNG 文件, 且不超过 500kb</div>
- </el-upload>
2.methods 添加 handleRemove 函数处理删除数据的功能
关于 handleRemove 函数要实现的功能, 前面我们已经讲过: 当删除一张图片后, 删除对应的 tableData.attachList 中的数据.
关于这个功能有两个办法可以实现:
1遍历 tableData.attachList 中的文件信息, 将需要删除的文件删除.
2on-remove 钩子函数在调用时有两个参数: file 和 fileList.
file 就是我们当前操作的文件, 对于删除操作, file 就是当前删除文件的信息;
fileList 是操作完成后 < el-upload > 控件的的所有文件列表.
因此, 可直接将 fileList 赋值给 tableData.attachList.
我们分别使用两种办法去实现.
方法一: 遍历 tableData.attachList 中的文件信息, 将需要删除的文件删除
- handleRemove(file, fileList){
- var currentIndex = this.currentIndex;
- var attachList = this.tableData[currentIndex].attachList;
- var tempList = [];
- for(var i = 0; i<attachList.length; i++){
- if(file.name != attachList[i].name){
- tempList.push(attachList[i]);
- }
- }
- this.tableData[currentIndex].attachList = tempList;
- }
方法二: 直接将 on-remove 钩子函数的参数 fileList 赋值给 tableData.attachList
- handleRemove(file, fileList){
- var currentIndex = this.currentIndex;
- this.tableData[currentIndex].attachList = fileList;
- }
可以任意选择一种实现, 效果均相同
四. 功能完善 - 验证文件名是否重复
element 的多文件上传控件对重复的文件名并没有任何限制.
这个也不符合我们实际的开发场景. 因此我们需要完善这个功能.
查看 element 文档, 我们可以看到一个 before-upload 钩子函数
因此我们可以给 < el-upload > 控件添加 before-upload 钩子函数, 在上传文件之前去判断文件是否重名, 若有重名则阻止上传.
1. 给 < el-upload > 控件添加 before-upload 钩子函数
- <!-- 将 < el-upload > 代码添加到 < el-dialog > 代码块中 -->
- <el-upload
- class="upload-demo"
- drag
- action="https://jsonplaceholder.typicode.com/posts/"
- :on-remove="handleRemove"
- :file-list="currentAttachList"
- :on-success="uploadSuccess"
- :before-upload="beforeUpload">
- <i class="el-icon-upload"></i>
- <div class="el-upload__text"> 将文件拖到此处, 或 < em > 点击上传 </em></div>
- <div class="el-upload__tip" slot="tip"> 只能上传 jpg/PNG 文件, 且不超过 500kb</div>
- </el-upload>
2.methods 定义 beforeUpload 函数
- beforeUpload(file){
- var currentIndex = this.currentIndex;
- // 首先需要获取当前已经上传的文件列表
- var list = this.tableData[currentIndex].attachList;
- // 循环文件列表判断是否有重复的文件
- for(var i = 0;i<list.length;i++){
- if(list[i].name == file.name){
- this.$message.error(file.name + '文件名重复');
- // 记得一定要返回 false, 否则控件继续会执行上传操作
- return false;
- }
- }
- }
现在看下效果:
可以看到当文件名称重复时, 会有一个错误提示并且成功阻止了这个重复文件的上传.
然而, 当我们在此查看文件列表时, 发现之前存在的文件在列表中丢失了.
这个原因是为啥呢? 因为当 bfeore-upload 返回 false 之后, 该组件会默认执行 before-remove 和 on-remove 这个两个钩子函数, 我们在使用这个控件的时候只添加了 on-remove 这个钩子函数, 为了证实这个默认行为, 我们把 before-remove 这个钩子函数加上, 并且在添加一些打印信息.
添加 before-remove 钩子函数
- <el-upload
- class="upload-demo"
- drag
- action="https://jsonplaceholder.typicode.com/posts/"
- :on-remove="handleRemove"
- :before-remove="beforeRemove"
- :file-list="currentAttachList"
- :on-success="uploadSuccess"
- :before-upload="beforeUpload">
- <i class="el-icon-upload"></i>
- <div class="el-upload__text"> 将文件拖到此处, 或 < em > 点击上传 </em></div>
- <div class="el-upload__tip" slot="tip"> 只能上传 jpg/PNG 文件, 且不超过 500kb</div>
- </el-upload>
methods 添加 beforeRemove 和一些打印信息
- beforeRemove(file, fileList){
- console.log('我是 before-remove 钩子函数, 我被调用了');
- },
- handleRemove(file, fileList){
- console.log('我是 on-remove 钩子函数, 我被调用了');
- var currentIndex = this.currentIndex;
- var attachList = this.tableData[currentIndex].attachList;
- var tempList = [];
- for(var i = 0; i<attachList.length; i++){
- if(file.name != attachList[i].name){
- tempList.push(attachList[i]);
- }
- }
- this.tableData[currentIndex].attachList = tempList;
- },
然后就刚刚上传重复文件的那个操作我们看下打印信息
可以看到我们前面的说法已经被证实了.
同时, 在深入一步思考一下, 因为阻止上传重复的文件名, 导致 on-remove 钩子函数被调用删除了对应 tableData.attachList 中的数据, 所以当我们在此点击查看文件列表时 [皇阿玛问号. jpg] 已经不存在了.
那如何解决这个问题呢?
首先我们先将 before-remove 这个钩子函数完善一下: 删除前给用户提示确认是否删除
- beforeRemove(file, fileList){
- return this.$confirm('此操作将永久删除' + file.name +'文件, 是否继续?');
- },
然后, 解决这个问题的关键是: 当 before-upload 返回 false 后, 不执行 before-remove 和 on-remove 这两个钩子函数里面的逻辑.
那我们看一下关于 before-remove 钩子函数的文档
可以看到, 该钩子函数可以通过返回 false 停止删除, 即可以阻止 on-remove 函数的调用.
所以我们将思路转到 before-remove 函数, 只要能在 before-remove 里面做出一些判断, 在上传重复的文件后使函数返回 false.
那么现在需要做的就是在 before-upload 中得知上传了重复文件后, 设置 isRepeat 标志值为 true, 在 before-remove 判断如果 isRepeat 这个标志值为 true, 就令该钩子函数返回 false 阻止 on-remove 函数的调用.
- data () {
- return {
- // 是否包含重复的文件名称, 默认不包含值为 false
- isRepeat: false
- }
- },
- methods: {
- beforeRemove(file, fileList){
- if(this.isRepeat == false){
- return this.$confirm('此操作将永久删除' + file.name +'文件, 是否继续?');
- }else{
- // 这个逻辑表示包含重复的文件, 这按照文档返回 false 可阻止文件继续上传
- return false;
- }
- },
- beforeUpload(file){
- var currentIndex = this.currentIndex;
- // 首先需要获取当前已经上传的文件列表
- var list = this.tableData[currentIndex].attachList;
- // 循环文件列表判断是否有重复的文件
- for(var i = 0;i<list.length;i++){
- if(list[i].name == file.name){
- this.$message.error(file.name + '文件名重复');
- // 添加逻辑: 得知上传了重复文件后, 设置一个标志值为 true, 提供给 beforeRemove 函数使用
- this.isRepeat = true;
- // 记得一定要返回 false, 否则控件继续会执行上传操作
- return false;
- }
- }
- }
- }
然后我们看下效果:
这个结果看到之后有些吐血.
虽然当有重复文件上传时有了错误提示, 但是这个重复发文件名却展示在了文件列表中. 查看了打印信息, 发现并没有调用 on-remove 钩子函数.
(这个文件上传控件这么鸡肋吗? 还是我用法有误?)
没办法, 也不知道啥原因, 我只能在想想办法.
在转了转脑子, 于是想尝试把 before-remove 中 else{ return false;} 逻辑删除, 这样当文件名称重复后, 会自动调用 on-remove 钩子函数, 我们把对 isRepeat 数据的判断加在 on-remove 钩子函数中去阻止删除操作.
- beforeRemove(file, fileList){
- if(this.isRepeat == false){
- return this.$confirm('此操作将永久删除' + file.name +'文件, 是否继续?');
- }
- },
- handleRemove(file, fileList){
- console.log('我是 on-remove 钩子函数, 我被调用了');
- if(this.isRepeat == false){
- var currentIndex = this.currentIndex;
- var attachList = this.tableData[currentIndex].attachList;
- var tempList = [];
- for(var i = 0; i<attachList.length; i++){
- if(file.name != attachList[i].name){
- tempList.push(attachList[i]);
- }
- }
- this.tableData[currentIndex].attachList = tempList;
- }
- },
再看下效果:
现在看起来这个效果是正常了, 重复的文件没有上传也没有展示到文件列表中, 在此点击查看也显示正常.
但是呢, 还有最后一个问题, 保证是最后一个问题了:
因为当文件重复后, isRepeat 设置为了 true, 之后在没有地方修改这个数据, 那么一个重复图片上传后, 我们操作删除文件, 此时 isRepeat 设置为 true, 按照逻辑 before-remove 中的删除提示不会执行, on-remove 中的删除逻辑也不会执行.
所以呢, 我们还需要在上传重复图片后, 将 this.isRepeat 还原为 false, 那么我们将代码添加到 on-remove 函数中即可.
- handleRemove(file, fileList){
- console.log('我是 on-remove 钩子函数, 我被调用了');
- if(this.isRepeat == false){
- var currentIndex = this.currentIndex;
- var attachList = this.tableData[currentIndex].attachList;
- var tempList = [];
- for(var i = 0; i<attachList.length; i++){
- if(file.name != attachList[i].name){
- tempList.push(attachList[i]);
- }
- }
- this.tableData[currentIndex].attachList = tempList;
- }else{
- this.isRepeat = false;
- }
- },
最后我们将打印信息删除, 贴上完整的代码
src/App.vue
- <template>
- <div id="app">
- <!-- element-ui Table 表格组件 -->
- <el-table
- class="my-table"
- :data="tableData"
- stripe
- style="width:725px;">
- <el-table-column
- prop="date"
- label="日期"
- width="180">
- </el-table-column>
- <el-table-column
- prop="name"
- label="姓名"
- width="180">
- </el-table-column>
- <el-table-column
- prop="address"
- label="地址"
- width="180">
- </el-table-column>
- <!-- 添加一列附件管理 (文件上传) -->
- <el-table-column
- prop="attach"
- label="附件管理"
- width="180">
- <template slot-scope="scope">
- <!-- 上传按钮绑定 click 事件 -->
- <el-button size='small' type="primary" @click="uploadBtnClick(scope.$index)">
上传
- <i class="el-icon-upload el-icon--right"></i>
- </el-button>
- </template>
- </el-table-column>
- </el-table>
- <el-dialog
- title="附件管理"
- :visible.sync="dialogVisible"
- width="30%">
- <!-- 将 < el-upload > 代码添加到 < el-dialog > 代码块中 -->
- <el-upload
- class="upload-demo"
- drag
- action="https://jsonplaceholder.typicode.com/posts/"
- :on-remove="handleRemove"
- :before-remove="beforeRemove"
- :file-list="currentAttachList"
- :on-success="uploadSuccess"
- :before-upload="beforeUpload">
- <i class="el-icon-upload"></i>
- <div class="el-upload__text"> 将文件拖到此处, 或 < em > 点击上传 </em></div>
- <div class="el-upload__tip" slot="tip"> 只能上传 jpg/PNG 文件, 且不超过 500kb</div>
- </el-upload>
- <span slot="footer" class="dialog-footer">
- <el-button @click="dialogVisible = false"> 取 消 </el-button>
- <el-button type="primary" @click="dialogVisible = false"> 确 定 </el-button>
- </span>
- </el-dialog>
- </div>
- </template>
- <script>
- export default {
- name: 'App',
- data () {
- return {
- // 添加属性, 默认值为 false, 表示弹框不显示
- dialogVisible: false,
- // 设置当前文件列表数据 currentAttachList, 每次用户点击上传按钮, 该数据就会被赋值为当前按钮那一列 tableData 中的 attachList 数据
- currentAttachList: [],
- // 当前点击打开弹框的按钮在表格中是那一列
- currentIndex: 0,
- // 是否包含重复的文件名称, 默认不包含值为 false
- isRepeat: false,
- tableData: [{
- date: '2016-05-02',
- name: '王小虎',
- address: '上海市普陀区金沙江路',
- attachList:[]
- }, {
- date: '2016-05-04',
- name: '王小虎',
- address: '上海市普陀区金沙江路',
- attachList:[]
- }, {
- date: '2016-05-01',
- name: '王小虎',
- address: '上海市普陀区金沙江路',
- attachList:[]
- }, {
- date: '2016-05-03',
- name: '王小虎',
- address: '上海市普陀区金沙江路',
- attachList:[]
- }]
- }
- },
- methods: {
- uploadBtnClick (index){
- // 获取上传按钮对应那一列表格数据中的附件列表, 赋值给 currentAttachList
- this.currentAttachList = this.tableData[index].attachList;
- // 将控制弹框显示的 dialogVisible 设置为 true, 让弹框显示
- this.dialogVisible = true;
- // 设置 currentIndex
- this.currentIndex = index;
- },
- uploadSuccess(response, file, fileList){
- var currentIndex = this.currentIndex;
- this.tableData[currentIndex].attachList.push({
- 'name':file.name
- });
- },
- beforeRemove(file, fileList){
- if(this.isRepeat == false){
- return this.$confirm('此操作将永久删除' + file.name +'文件, 是否继续?');
- }
- },
- handleRemove(file, fileList){
- if(this.isRepeat == false){
- var currentIndex = this.currentIndex;
- var attachList = this.tableData[currentIndex].attachList;
- var tempList = [];
- for(var i = 0; i<attachList.length; i++){
- if(file.name != attachList[i].name){
- tempList.push(attachList[i]);
- }
- }
- this.tableData[currentIndex].attachList = tempList;
- }else{
- this.isRepeat = false;
- }
- },
- beforeUpload(file){
- var currentIndex = this.currentIndex;
- // 首先需要获取当前已经上传的文件列表
- var list = this.tableData[currentIndex].attachList;
- // 循环文件列表判断是否有重复的文件
- for(var i = 0;i<list.length;i++){
- if(list[i].name == file.name){
- this.$message.error(file.name + '文件名重复');
- // 添加逻辑: 得知上传了重复文件后, 设置一个标志值为 true, 提供给 beforeRemove 函数使用
- this.isRepeat = true;
- // 记得一定要返回 false, 否则控件继续会执行上传操作
- return false;
- }
- }
- }
- }
- }
- </script>
- <style>
- #App {
- font-family: 'Avenir', Helvetica, Arial, sans-serif;
- -webkit-font-smoothing: antialiased;
- -moz-OS X-font-smoothing: grayscale;
- color: #2c3e50;
- margin: 50px 30px;
- text-align: center;
- }
- #App .el-dialog__header{
- background:#EBEEF5;
- border-bottom: 1px solid#EBEEF5;
- }
- #App .el-dialog{
- text-align: left;
- }
- #App .el-upload,#App .el-upload .el-upload-dragger{
- width: 100%;
- }
- #App .el-upload-list li{
- transition: none;
- }
- </style>
- <style scoped>
- #App .my-table{
- display: inline-block;
- border: 1px solid #EBEEF5;
- }
- </style>
最后在来操作一波
五. 总结
到此,《使用 element 的 upload 组件实现一个完整的文件上传功能》完成, 该功能是结合前段时间在实际项目开发中做的一个功能, 在这里单独拿出来总结.
在整个实践过程中, 个人感觉 element 的 upload 组件, 对多文件的上传功能还是不太友好, 两个至今还没有探究明白的问题, 在文中也以红色字体标出.
这两个问题虽然看着不影响什么, 但心里总是有些不踏实.
来源: https://www.cnblogs.com/HouJiao/p/12125313.html