艺灵设计

全部文章
×

Vue3踩坑系列之Element-Plus库中upload上传组件的坑(下)

作者:艺灵设计 - 来源:http://www.yilingsj.com - 发布时间:2022-09-17 21:40:31 - 阅: - 评:0 - 积分:0

摘要:上一篇中,我把Element-Plus组件库从1.0.2-beta.53升级到了2.2.16版本,遇到了2个问题并解决了。在后续调试过程中发现upload组件仍无法正常使用,因为还有一些别的问题需要解决

话说上周我吐槽了因升级Element-Plus导致upload组件踩坑一事,当时只解决了插槽file监听file-list这两个问题。在后续优化中,一个个坑陆续而来,于是便有了这篇文章。

一、坑一:父组件中调用子组件上传功能失效了

为了让看官更清楚的了解需求,先放一张效果图↓↓↓点击父组件中的上传按钮触发子组件上传功能

上图最大的特点就是:不直接点击el-upload上传组件,通过调用组件中的方法实现了上传的功能。相信这个功能也是很多看官在工作中经常用到的,有点隔山打牛的意思。

这个功能在Vue2 + Element-UI时是好的,在Vue3 + Element-Plus@1.0.2-beta.53我做了次兼容,现在升级到Element-Plus@2.x后彻底不能用了。[:苦涩]

经过一段时间的折腾,最终解决了问题。现把几个版本的代码分享出来,方便日后自己使用。

1.1、Vue2 + Element-UI时的代码

<!-- 友情提示:因编码原因,文章中的双括号{}均做了转码处理,复制后需要替换成英文状态下的{ }才能运行 -->
<!-- 弹窗↓↓↓ -->
<el-dialog width="970px" :title="title" :visible.sync="dialogVisible" v-if="dialogVisible" append-to-body>
  <el-col>
    <el-button @click.stop="handleClickStop({ type: 'triggerUpload' })"> 本地上传 </el-button>
    <span v-if="uploadFilesLength"> ({{ uploadFilesLength }​}张)</span>
  </el-col>

  <!-- 这是封装的上传组件↓↓↓ -->
  <UploadTem ref="picUploadTem" multiple :autoUpload="false" :limit="15"> </UploadTem>
  <!-- 这是封装的上传组件↑↑↑ -->

  <div class="el-dialog__footer flex flex-end">
    <el-button @click="handleDialogCancle">取 消</el-button>
    <el-button type="primary" @click.stop="handleClickStop({ type: 'bulkUpload' })">批量上传</el-button>
  </div>
</el-dialog>

<script>
export default {
  methods: {
    /**
     * @author: yiling (315800015@qq.com)
     * @description: 公用点击事件
     * @param {Object} options 自定义对象
     * @return {*}
     * @Date: 2021-06-09 17:33:32
     */
    handleClickStop(options) {
      const { type } = options
      console.log('ref=', this.$refs)
      switch (type) {
        case 'triggerUpload': // 手动触发选择图片
          this.$refs.picUploadTem.handleUpdate() // handleUpdate为子组件中的方法
          break
        case 'bulkUpload': // 开始批量上传
          // ...... // 省略一些代码
          break
        default:
          break
      }
    },
  },
}
</script>

下面再来看下子组件中的代码。

<!-- 友情提示:因编码原因,文章中的双括号{}均做了转码处理,复制后需要替换成英文状态下的{ }才能运行 -->
<template>
  <div>
    <el-upload :ref="refName" :list-type="listType">
      <div v-if="listType === 'picture-card' && !$slots.trigger">
        <i class="el-icon-plus"></i>
        <div class="el-upload-name">{{ title }​}</div>
      </div>

      <!-- 此处省略若干代码↓↓↓ -->
      ......
      <!-- 此处省略若干代码↑↑↑ -->

      <slot name="trigger"></slot>
      <slot name="tip"></slot>
    </el-upload>

    <!-- 预览图片功能↓↓↓ -->
    <div class="demo-image__preview" v-if="dialogImageUrl">
      <el-image class="hidden__el-image" ref="elImage" :src="dialogImageUrl" :preview-src-list="previewSrcList">
      </el-image>
    </div>
  </div>
</template>

<script>
export default {
  methods: {
    /**
     * @author: yiling (315800015@qq.com)
     * @description: 执行上传
     * @param {*}
     * @return {*}
     * @Date: 2021-04-28 16:18:57
     */
    handleUpdate() {
      this.$refs[this.refName].$refs['upload-inner'].$refs.input.click() // 调用el-upload组件中内置的方法
    },
  }, // 挂载一些方法
}
</script>

在上面这段代码中,最重要的就是handleUpdate()函数。通过$refs['upload-inner']一层层向下找到input并调用click()事件,实现了打开资源管理器进行选择图片资源的功能。如果类型不限制,别的资源也可进行选择。

1.2、Vue3 + Element-Plus@1.0.2-beta.53时的代码

当项目初次升级后,此时Element-Plus对应的版本号是1.0.2-beta.53。再点击“本地上传”按钮,结果报错了。

Uncaught TypeError: Cannot read properties of undefined (reading '$refs')
at Proxy.handleUpdate (index.vue:351:54)
at Proxy.handleClickStop (index.vue:687:37)

经调试发现问题出在$refs['upload-inner']身上,此时是没有$refs['upload-inner']的,所以下一级的.$refs自然就报错了。

一层层点开$refs,当看到uploadRef下面有handleClick()inputRef原生dom时,我觉得有希望了。如下图↓↓↓在refs中找到了uploadRef

经测试证明此法确实可行。

<script>
  handleUpdate() {
    // this.$refs[this.refName].$refs['upload-inner'].$refs.input.click() // Vue2的写法
    this.$refs[this.refName].$refs.uploadRef.handleClick() // Element-Plus@1.0.2-beta.53时的写法
  }
</script>

1.3、Vue3 + Element-Plus@2.x时的代码

当升级Element-Plus@2.x后,再次打印$refs,你会看到暴露出来的东西少的可怜!elUpload.$refs也没有了!此时打印$refs结果如下图↓↓↓Element-Plus@2x中没有暴露出$refs

这就尴尬了。。。。。。

继续去看官方文档,发现官方演示了一个手动上传的功能。如果我能控制这个按钮不就好了嘛!

于是我在子组件中添加了插槽trigger并给按钮设置了ref="triggerRef"。此时再打印$refs,有了新的发现。如下图↓↓↓打印$refs可看到相关数据

一层层点开后,我看到了triggerRef下面有个ref,突然间就觉得有戏了。继续点开发现value指向了我们定义的按钮。接下来就好办了,直接调用click事件,看下能否达到预期效果。

经测试发现,此方案确实可行。最终可行代码见下方。

<slot name="trigger">
  <!-- 这个按钮是为了方便外部触发upload的选择资源功能,兼容高版本的Element-Plus ↓↓↓ -->
  <el-button ref="triggerRef" class="triggerRef" type="primary">select file</el-button>
  <!-- 这个按钮是为了方便外部触发upload的选择资源功能,兼容高版本的Element-Plus ↑↑↑ -->
</slot>

<script>
  handleUpdate() {
    // this.$refs[this.refName].$refs['upload-inner'].$refs.input.click() // Vue2的写法
    // this.$refs[this.refName].$refs.uploadRef.handleClick() // Element-Plus@1.0.2-beta.53时的写法
    this.$refs.triggerRef.ref.click() // 兼容高版本的Element-Plus
  }
</script>

再点击“本地上传”按钮,终于可以弹窗选择图片了。

上传图片后鼠标滑过图片,新问题出现。

二、坑二:icon图标都不显示了

先放张正常情况下的动态效果图↓↓↓icon图标恢复正常了

当我把Element-Plus升级到@2.x后,图标全部无法显示!

翻看官方文档,发现高版本的Element-Plus已经废弃了字体图标库,改用SVG了。

解决方案也很简单,按照官方提示,安装相关图标库并做相关修改即可。

2.1、手动安装@element-plus/icons-vue

npm install @element-plus/icons-vue --save-dev // 先安装图标库

安装后还需要修改main.js文件。

import * as ElementPlusIconsVue from '@element-plus/icons-vue'

const app = createApp(App)
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
  app.component(key, component)
}

前面两步完成后,接下来把页面中的老写法改成新的即可。

<!-- 之前的写法,兼容vue2、vue3+Element-Plus@1.0.2-beta.53 ↓↓↓ -->
<i class="el-icon-plus"></i>

<!-- Element-Plus高版本时的写法↓↓↓ -->
<el-icon><Plus /></el-icon>

上面的<Plus>对应原来的el-icon-plus

我原以为只需要把所有的el-icon-前缀去掉,再把后面的单词的首字母改成大写就能兼容Element-Plus。吭哧吭哧改完后,结果发现部分页面中的图标仍无法显示!

。。。。。。

在对比Element-UIElement-Plus的图标库后,我发现这一转换是有规律的,但有个别例外。详细见下表。

Element-UI中的图标转换为Element-Plus图标
Element-UIElement-Plus转换规律
<i class="el-icon-plus"></i><Plus />大多数图标可通过去掉el-icon-前缀,并把剩下的首字母进行大写即可兼容Element-Plus
<i class="el-icon-zoom-in"></i><ZoomIn />当剩余字母中出现-时,一般继续使用驼峰写法。
注意el-icon-s-home是例外
<i class="el-icon-switch-button"></i><SwitchButton />
<i class="el-icon-s-home"></i><HomeFilled />如果Element-UI图标背景为非镂空时,转换后需要加上Filled
<i class="el-icon-info"></i><InfoFilled />
<i class="el-icon-error"></i><CircleCloseFilled />
<i class="el-icon-more"></i><MoreFilled />
<i class="el-icon-warning"></i><WarningFilled />
<i class="el-icon-edit"></i><EditPen />这个我就看不出来规律了,难道是因为像笔?
..................

上面表格中的图标渲染后见下图↓↓↓图标转换后正常显示

现在,鼠标再滑过图片,此时成功显示“编辑”、“查看”和“删除”这三个图标。

三、坑三:image预览功能失效

在未升级到@2.x时,当我们点击上一步中的“查看”图标,是可以正常预览图片的。相关代码见下方。

3.1、Vue2+Element-UI时的代码

<template>
  <div>
    <!-- 此处省略若干代码↓↓↓ -->
    ......
    <!-- 此处省略若干代码↑↑↑ -->

    <!-- 预览图片功能↓↓↓ -->
    <div class="demo-image__preview" v-if="dialogImageUrl">
      <el-image class="hidden__el-image" ref="elImage" :src="dialogImageUrl" :preview-src-list="previewSrcList">
      </el-image>
    </div>
  </div>
</template>

<script>
export default {
  methods: {
    /**
     * @author: yiling (315800015@qq.com)
     * @description: 点击“查看”进行预览图片
     * @param {Object} file file对象
     * @return {*}
     * @Date: 2021-04-27 16:51:54
     */
    handlePictureCardPreview(file) {
      this.dialogImageUrl = this.computedGetPictureSrc(file.url) // 拼接图片地址
      this.$nextTick(() => {
        this.$refs.elImage.clickHandler() // 主要是这行代码
      })
    },
  }, // 挂载一些方法
}
</script>

3.2、Vue3+Element-Plus@1.0.2-beta.53时的代码

代码同3.1,故不再赘述。

我们来看点有用的信息,在控制台中打印$refs,结果如下图↓↓↓Element-Plus@1x时打印refs信息

可以看到在Element-Plus@1.0.2-beta.53版本时,elImage中仍暴露出很多信息,包括这个内置的clickHandler方法

当我升级Element-Plus@2.x后,点击“查看”图标时,控制台出现了报错。

Uncaught (in promise) TypeError: this.$refs.elImage.clickHandler is not a function
    at Proxy.

打印$refs结果如下图↓↓↓Element-Plus@2x时没有clickHandler方法

现在这个内置的clickHandler不再暴露出来,也就意味着我无法通过点击“查看”图标触发image预览功能

这。。。。。不行啊!

又去官网看了下相关的文档,发现了Image Viewer API。经过一翻尝试,成功解决问题。

3.3、Vue3+Element-Plus@2.x时的代码

<!-- 预览图片功能↓↓↓ -->
<div class="demo-image__preview" v-if="dialogImageUrl">
  <!-- 注意这里由原来的 el-image 改为 el-image-viewer ↓↓↓ -->
  <el-image-viewer
    class="hidden__el-image"
    ref="elImage"
    :src="dialogImageUrl"
    :url-list="previewSrcList"
    @close="handleDialogCloseImage"
  >
  </el-image-viewer>
</div>

<script>
export default {
  methods: {
    /**
     * @author: yiling (315800015@qq.com)
     * @description: 点击“查看”进行预览图片
     * @param {Object} file file对象
     * @return {*}
     * @Date: 2021-04-27 16:51:54
     */
    handlePictureCardPreview(file) {
      const index = this.newFileList.findIndex(item => item.uid === file.uid) // 计算出索引
      this.dialogImageUrl = this.computedGetPictureSrc(file.url) // 拼接图片地址
      this.$nextTick(() => {
        // this.$refs.elImage.clickHandler() // Vue2、Vue3+Element-Plus@1.0.2-beta.53时可用
        this.$refs.elImage.setActiveItem(index) // Element-Plus高版本时可用
      })
    },
  }, // 挂载一些方法
}
</script>

现在,点击“查看”图标,又能正常预览图片了,欧耶!

解决问题的方法也很简单,将<el-image>标签换成内置的<el-image-viewer>,此时可以通过暴露的setActiveItem事件进行手动切换图片,从而实现预览图片。此时的$refs打印信息见下图↓↓↓el-image-viewer组件内置setActiveItem方法

四、小结

说真的,在项目中选择一个稳定版本的三方UI库还是很有必要的,至少能够保证项目在某一大段时间内的稳定性。可Element-Plus到现在为止也没出个稳定版,昨天(2022.09.16)还发布了2.2.17版。

对于公司的这个项目,我没有继续升级的打算。

Element-Plus版本定在2.2.16就好了。在此版本上,如果老代码有不兼容的地方,想方设法也要给解决掉。剩下的需求及迭代,我就可以放手给其他同事了。

转载声明:
  若亲想转载本文到其它平台,请务必保留本文出处!
本文链接:/xwzj/2022-09-17/vue3-element-plus-upload2.html

若亲不想直保留地址,含蓄保留也行。艺灵不想再看到有人拿我的技术文章到他的地盘或者是其它平台做教(装)程(B)而不留下我的痕迹。文章你可以随便转载,随便修改,但请尊重艺灵的劳动成果!谢谢理解。

亲,扫个码支持一下艺灵呗~
如果您觉得本文的内容对您有所帮助,您可以用支付宝打赏下艺灵哦!

Tag: Vue3 踩坑系列 项目优化 Vue vite Element-Plus upload 上传组件 插槽 svg

上一篇: Vue3踩坑系列之Element-Plus库中upload上传组件的坑   下一篇: Vue3踩坑系列之把Element-UI升级到Element-Plus时遇到的若干坑

评论区