需求:通过按钮的点击事件控制另一个输出框/按钮的点击

目录

第一章 接到需求

第二章 了解需求

第三章 解决需求 

第四章 优化代码

第五章 解决问题


第一章 接到需求

  • 最近开发的时候遇到这么一个事,技术经理是个全栈,已经把接口生成了,而且前端页面也写好了一个初稿,操作什么的功能基本上已经布局好了,如下:

经理说需求了:这里有两个按钮,一个是导入文件的,一个是导入机构的,你看看怎么把这按钮功能实现,把excel文件导入进去,后台的接口已经写好了 

第二章 了解需求

  • 然后小编接到需求了,clone下代码,根据路由找到对应的页面代码(注释的部分是小编已经写好的代码):

  •  发现这里就是两个按钮,然后设置了权限一些其他配置。
  • (注意事项:我们很多人有的时候为了语义化一些特别容易出现一个问题:就是会用到关键字,比如小编一开始看这个代码的时候,上面写的是@click="import(scope.row.id)",好家伙,不细看不知道,代码一跑吓一跳,直接控制台报错,一定要留意,import是关键字,我们不要直接定义!!!语义化命名不要命名到关键字!!!
  • 好了,接下来进入正题:当我们拿到这么一个需求,首先我们分析一下,很明显,我们拿到的需求是一个上传文件的功能实现,平常我们上传文件很多时候会用到element、antd等等一些框架类的东西来实现这么一个功能。那么问题来了,我们这里就只有按钮,样式、布局都确定了,而且不管我们怎么点击,都没反应。那么接下来我们考虑的是不是给按钮添加点击事件 -> 调出本地文件管理器 -> 点击我们需要上传的文件 -> 上传成功拿到文件的二进制流 -> 准备上传的参数 -> 像后端发送请求 -> 后端返回成功/失败的数据 -> 前端拿到数据进行处理,那么难点在哪呢:一个按钮,怎么能调出本地的文件管理器呢,又怎么知道我们点击文件上传了呢,对于用很多组件的我们来说可能就麻烦了,去找组件啥啥啥,但是小编觉得,是的,组件或许能帮我们实现这么个需求,但是我们难道没有别的方法了吗?
  • 原生标签:input!!!小编回忆,最开始学习html的时候有个标签input,小编也不绕圈了,设置type="file"就是文件上传的按钮!!

  • 回忆一下,看如下文档:

HTML input type 属性 | 菜鸟教程

第三章 解决需求 

  • 好了,用到的工具有了,现在的问题在于如何解决我们利用导入的按钮操作文件上传的按钮?
  • 思路:小编为按钮与input都绑定唯一识别的ref(vue)/id(正常的html页面)以及click点击事件,原生js会告诉我们,当我们绑定id时,获取到DOM之后,我们能通过DOM.click获取到DOM上绑定的点击事件。问题解决!
  • 大家根据小编的代码理解逻辑:(小编会逐步解释的) 

html: 

// 这里是element-ui的button组件
<el-button 
    v-if="hasPermission('book:book:import')" // 通过权限控制图标显示隐藏(可忽略)
    type="text" //button的type(可忽略)
    icon="el-icon-import" // 图标类名(可忽略)
    size="small" // 尺寸
    @click="toImportBookList(scope.row.id)"> // 按钮的点击事件,传的参数是该行数据的id
    导入</el-button>
// 原生的input
<input 
    type="file" // 上传文件的类型
    accept=".xls,.xlsx" // 限制上传的文件格式
    :ref="`upload-book-list-file-${scope.row.id}`"  // 唯一识别的ref,利用id使得渲染时ref不重名
    style="display: block" // 控制该标签的显示隐藏
    @click="importBookList(scope.row.id)"> // 该标签的点击事件

js:(注意一定要理解思路,而不是直接拷贝代码)

export default {
  methods: { // vue:方法
    toImportBookList (id) { // 按钮的点击事件
      const bookListFileUplodDom = this.$refs[`upload-book-list-file-${id}`] // 通过$refs获取对应的input标签
      bookListFileUplodDom.click() // 注意这句代码,他就是通过dom调用自身的点击事件
    },
    importBookList (id) { // input标签的点击事件
      const _this = this
      const bookListFileUplodDom = this.$refs[`upload-book-list-file-${id}`] // 通过$refs获取input标签
      bookListFileUplodDom.addEventListener('change', function (e) { // input标签监听事件,当input发生变化时说明文件要么上传了,要么删除了,上传成功执行如下代码
        const file = bookListFileUplodDom.files[0] // 该方法只针对input、type="file"或者图片,才能使用他获取到file的二进制流
        const formBody = new FormData() // 这里的formData小编就不多说了,已经用到很多次了,小编会有单独的详细的文章做讲解
        formBody.append('file', file)
        formBody.append('id', id)
        this.loading = true // 添加加载中(可忽略)
        bookService.importBook(formBody).then(({data}) => { // 这里是发送请求系列信息,仅供参考
          this.$message.success({ // 请求成功后返回信息
            dangerouslyUseHTMLString: true,
            message: data
          })
          this.refreshList() // 刷新列表
        }).catch(err => { // 出错了则捕获错误
          this.loading = false // 隐藏加载中
          console.log('出错了', err)
        })
      }, false)
    }
  }
}

第四章 优化代码

  • 使用e.stopPropagation()

阻止事件冒泡,使用addEventListener,三个参数,第三个参数小编设置了false,表示的是事件冒泡,由里向外触发,子元素的点击事件触发之后,之后还会触发父元素的点击事件,从而导致我们点击事件执行到不需要执行的事件,触发顺序:children -> parent -> body,添加e.stopPropagation()就只会执行我们点击的标签的点击事件。代码如下:

bookListFileUplodDom.addEventListener('change', function (e) {
  e.stopPropagation() // 阻止冒泡事件(注意该方法写在代码前面)
  ……(其余代码逻辑)
}, false)
  • addEventListener我们注册的监听'change'函数有两种情况:当上传文件成功时,input变化了,会执行代码,然后向发送请求,当然,当我们删除文件时,文件都为空了,还需要执行代码,向后端发请求吗?所以需要我们判断一下,当删除文件时,不执行代码,不向后端发送请求

 

 通过输出可以看到,删除文件时,file拿到的值是undefined,那么我们就能通过判断来解决该问题了,代码如下:

bookListFileUplodDom.addEventListener('change', function (e) {
  e.stopPropagation() // 阻止冒泡事件(注意该方法写在代码前面)
  const file = bookListFileUplodDom.files[0]
  if(file){ // 只有当file有数据时才执行如下逻辑
    ……(其余代码逻辑)
  }
}, false)
  •  addEventListener注册了还需要removeEventListener移除事件,如何removeEventListener移除呢,在哪移除呢,解决方法在下一章节

第五章 解决问题

  • 查看问题,不如不添加removeEventListener的效果,当我们执行多次导入时,也就相当于注册了很多个addEventListener('change',function(e){})事件,就会造成当我们只执行一次有效操作时,多个监听开始执行,导致最终前端会发送多个相同的请求

 

  • (这里直接给出小编的最终解决完成的源代码,如果大家进一步知道原理,小编会在下一篇文章中说明原因,做详细的解释!)
  • 原理看该文章

js基础:addEventListener与removeEventListener使用时,涉及的问题(包括事件捕获、冒泡,removeEventListener不生效问题)-CSDN博客

<script>

let bookListHandler = null // 定义一个自变量的等待赋值(移除监听函数使用)

export default {

  methods: {
    toImportBookList (id) {
      const bookListFileUplodDom = this.$refs[`upload-book-list-file-${id}`]
      bookListFileUplodDom.removeEventListener('change', bookListHandler, false) // 每次调用函数执行移除上一次的添加的监听
      bookListFileUplodDom.click()
    },
    importBookList (id) {
      const _this = this
      const bookListFileUplodDom = this.$refs[`upload-book-list-file-${id}`]
      console.log('bookListFileUplodDom', bookListFileUplodDom)
      bookListHandler = function (e) { // 将执行的监听函数赋值
        e.stopPropagation()
        _this.getBookList(id, bookListFileUplodDom)
      }
      bookListFileUplodDom.addEventListener('change', bookListHandler, false) // 添加监听事件
    },
    getBookList (id, bookListFileUplodDom) {
      const file = bookListFileUplodDom.files[0]
      console.log('file', file)
      if (file) {
        const formBody = new FormData()
        formBody.append('file', file)
        formBody.append('id', id)
        this.loading = true
        bookService.importBook(formBody).then(({data}) => {
          this.$message.success({
            dangerouslyUseHTMLString: true,
            message: data
          })
          this.refreshList()
        }).catch(err => {
          this.loading = false
          console.log('出错了', err)
        })
      }
    },
  }
}
</script>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值