Vue+ElementUI实现复制粘贴上传图片功能

Vue+ElementUI实现复制粘贴上传图片功能

背景

前后端分离框架
前端:Vue-element-admin
后端:Spring框架
场景:在保留原有组件的上传功能时,新增一个通过复制粘贴上传图片的功能。并且该按钮是在列表中,每一个列都会有一个上传按钮。
在查阅博客后未找到直接实现该功能的代码,踩了一些坑,记录一下。

实现过程

现有项目使用的是Element-ui最简单的通过按钮上传图片,支持基础的预览、删除等功能。
element-ui原始上传
原始代码:

<el-table-column label="上传附件" width="130">
              <template slot-scope="scope">
                <el-upload
                  :action="uploadUrl"
                  :on-preview="handlePreview"
                  :before-remove="beforeRemove"
                  :on-remove="(res) => { return onRemove(res, scope.row) }"
                  :data="uploadData"
                  :multiple="true"
                  :limit="9"
                  :on-exceed="handleExceed"
                  :file-list="scope.row.compensateFiles"
                  :before-upload="handleUpload"
                  :name="'uploadFiles'"
                  :on-success="(res) => { return uploadSuccess(res, scope.row) }"
                  auto-upload
                >
                  <el-button size="small" type="primary">上传</el-button>
                </el-upload>
                <el-dialog :visible.sync="picVisible" :append-to-body="true" @close="closePicView()">
                  <img width="80%" :src="dialogImageUrl" alt="">
                </el-dialog>
              </template>
            </el-table-column>

改动后代码:
Vue代码:

           <el-table-column label="上传附件" width="130">
              <template v-slot="scope">
                <el-upload
                  :ref="'upload' + scope.$index"
                  :action="uploadUrl"
                  :on-preview="handlePreview"
                  :before-remove="beforeRemove"
                  :on-remove="(res) => { return onRemove(res, scope.row) }"
                  :data="uploadData"
                  :multiple="true"
                  :limit="9"
                  :on-exceed="handleExceed"
                  :file-list="scope.row.compensateFiles"
                  :before-upload="handleUpload"
                  :name="'uploadFiles'"
                  :on-success="(res) => { return uploadSuccess(res, scope.row) }"
                  auto-upload
                >
                  <el-button size="small" type="primary">上传</el-button>
                  <!-- 每个上传按钮对应一个隐藏的输入框 -->
                  <input
                    ref="pasteInputs"
                    type="text"
                    style="position: absolute; left: -9999px;"
                    @paste="(event) => handlePaste(event, scope.row, scope.$index)"
                  >
                </el-upload>
                <el-dialog :visible.sync="picVisible" :append-to-body="true" @close="closePicView()">
                  <img width="80%" :src="dialogImageUrl" alt="">
                </el-dialog>
              </template>
            </el-table-column>

JS代码:

    handlePaste(event, row, index) {
      const me = this
      const items = (event.clipboardData || window.clipboardData).items
      for (const item of items) {
        if (item.type.indexOf('image') !== -1) {
          const file = item.getAsFile()
          // 使用动态 ref 获取对应的 el-upload 组件实例
          const uploadComponent = this.$refs['upload' + index]
          // 手动触发上传
          if (uploadComponent) {
            const formData = new FormData()
            formData.append('uploadFiles', file)
            formData.append('traceId', this.form.refundSerialNo)

            multipartUpload(formData).then(
              (response) => {
                if (response.success) {
                  if (Array.isArray(response.data.successFiles) && response.data.successFiles.length > 0) {
                    let rowCompensateFile
                    response.data.successFiles.forEach(file => {
                      var compensateFile = {
                        fileId: file.fileId,
                        name: file.fileName,
                        url: file.fileUrl,
                        passengerGuid: row.passengerGuid,
                        passengerName: row.passengerName
                      }
                      if (!Array.isArray(me.form.showCompensateItems[index].compensateFiles)) {
                        rowCompensateFile = []
                      } else {
                        rowCompensateFile = [...me.form.showCompensateItems[index].compensateFiles]
                      }
                      rowCompensateFile.push(compensateFile)
                      Vue.set(me.form.showCompensateItems[index], 'compensateFiles', rowCompensateFile)
                    })
                  }
                  if (Array.isArray(response.data.failFiles) && response.data.failFiles.length > 0) {
                    var failMsg = ''
                    // 失败提示
                    response.data.failFiles.forEach(failFile => {
                      failMsg += failFile.fileName
                    })
                    this.$message.warning(`上传失败,乘客姓名 ${row.passengerName} 失败文件 ${failMsg} `)
                  }
                }
              },
              () => {
              }
            )
          }
        }
      }
    }

开发过程中遇到的问题

1.如何监听粘贴事件
在button后面新增一个隐藏的输入框,通过输入框的@paste事件监听粘贴

                  <input
                    ref="pasteInputs"
                    type="text"
                    style="position: absolute; left: -9999px;"
                    @paste="(event) => handlePaste(event, scope.row, scope.$index)"
                  >

2.如何手动触发上传
因为现在要求需要兼容原有的逻辑,而后端代码的接口协议无法改变,手动触发上传时需要以form-data的形式调用接口。
后端接口协议:

    @PostMapping(value = "/multipartUpload")
    public SingleResponse multipartUpload(FileUploadParam fileUploadParam) 

Js代码:需要创建一个FormData对象通过append进行处理后进行调用,才能调通,否则会获取不到传参的值。

            const formData = new FormData()
            formData.append('uploadFiles', file)
            formData.append('traceId', this.form.refundSerialNo)
            multipartUpload(formData).then()

3.在列表中如何获取对应的组件实例,然后进行处理
在组件中的ref指定时使用index进行处理

 <el-upload :ref="'upload' + scope.$index" />

获取实例

const uploadComponent = this.$refs['upload' + index]

4.改变了:file-list对应的值,但是没有在页面上渲染出来。
原始代码:直接修改compensateFiles的值时发现没有渲染到页面,一开始一直怀疑是值没改变,通过debug发现值是改变了,但是一直没有触发渲染。

            if (!Array.isArray(row.compensateFiles)) {
              row.compensateFiles = []
            }
            row.compensateFiles.push(compensateFile)

vue 无法检测 property 的添加或移除。由于 Vue 会在初始化实例时对 property 执行 getter/setter转化,所以 property 必须在 data 对象上存在才能让 Vue 将它转换为响应式的。对于已经创建的实例,Vue允许动态添加根级别的响应式 property。
改动后代码:通过Vue.set方法,能够实现实时渲染。

                      if (!Array.isArray(me.form.showCompensateItems[index].compensateFiles)) {
                        rowCompensateFile = []
                      } else {
                        rowCompensateFile = [...me.form.showCompensateItems[index].compensateFiles]
                      }
                      rowCompensateFile.push(compensateFile)
                      Vue.set(me.form.showCompensateItems[index], 'compensateFiles', rowCompensateFile)

5.数组的复制问题
在开发的过程中,发现对于js中数组的创建不太熟悉
此处我想实现的效果是原有没值时,就创建一个空的数组,如果有值,则直接进行赋值。忘记了compensateFiles本来就是一个数组,通过new Array是创建了嵌套数组。
原有代码:

                      if (!Array.isArray(me.form.showCompensateItems[index].compensateFiles)) {
                        rowCompensateFile = []
                      }else {
                        rowCompensateFile = new Array(me.form.showCompensateItems[index].compensateFiles)
                      }

改进后代码:

                      if (!Array.isArray(me.form.showCompensateItems[index].compensateFiles)) {
                        rowCompensateFile = []
                      } else {
                        rowCompensateFile = [...me.form.showCompensateItems[index].compensateFiles]
                      }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值