[前端工程化 - 组件封装]图片上传组件

前言

大家好,我是辉夜真是太可爱啦,这是我即将最新开启的组件封装或基于组件库二次封装系列,代码均以 vue2 为例,组件库以 vant 为栗子

组件的封装在工作中使用的用处还是很大的,主要体现在维护的方便,可以将所有固定的代码抽到同一个组件中进行统一管理。

封装目的

这次是实现的是图片上传的功能,由于 vant 组件的 uploader 组件的 v-model 设计不太合理。

一般来说,正常单张图片的值是一个 String ,例如 https://xxxx.jpg

然后一串图片是一个 Array ,例如 ['https://xxxx.jpg' , 'https://xxxx.jpg']。

然后我们仅通过一个 v-model 即可绑定这个值,无需对图片上传之后值的更新,图片删除之后值的清空,或者是默认值的渲染再做多余的管理,可以大大的提升图片相关功能的开发效率。

后期还可以配合写一个 form 组件,使用 json 生成 form 表单。

Props

  • v-model 绑定的图片的值
  • max-count 绑定的最大值,1为单张图,大于1为列表
  • tips 文件上传提示语

Slot

  • defautl 默认插槽,用于修改图片上传的控件

开始封装

先书写 template 板块,关于插槽内的图片上传框的样式请自行修改。

html 复制代码
<template>
  <div class="image-uploader-component-main-box">
    <van-uploader
      :after-read="afterRead"
      :max-size="1024 * 1024 * 10"
      @delete="deleteFile"
      v-model="fileList"
      :max-count="maxCount"
      accept="image/png,image/jpeg,image/jpg"
    >
      <slot>
        <div class="upload-box">
          <span>图片上传</span>
        </div>
      </slot>
    </van-uploader>
  </div>
</template>

接下来,就是封装 js 的部分,由于fileList的格式是固定的,所以接下来需要用一个currentValue来进行绑定,fileList的数组,在初次渲染的时候,需要将currentValue中的值赋值给fileList,这样子才能保持两边数组的同步。

当然,uploadImage 图片上传的调用行为,请自行修改。

javascript 复制代码
<script>
export default {
  name: 'ImageUploader',

  props: {
    value: {
      type: [Array, String],
      default: '',
    },
    maxCount: {
      type: Number,
      default: 1,
    },
  },

  data() {
    return {
      currentValue: this.value,
      fileList: [],
      isInit: false, // 将一开始的默认值赋值给 fileList
    }
  },

  watch: {
    value: {
      handler(newValue) {
        if (!this.isInit) {
          if (this.maxCount === 1) {
            this.fileList = [
              {
                url: newValue,
                status: 'success',
                message: '上传成功',
              },
            ]
          } else {
            newValue.forEach((item) => {
              this.fileList.push({
                url: item,
                status: 'success',
                message: '上传成功',
              })
            })
          }
        }
        this.currentValue = newValue
      },
    },
  },

  methods: {
    afterRead(file) {
      this.isInit = true
      file.status = 'uploading'
      file.message = '上传中...'
      const formData = new FormData()
      formData.append('file', file.file)
      uploadImage(formData)
        .then((res) => {
          if (res.url) {
            file.status = 'success'
            file.message = '上传成功'
            file.url = res // FIXME: 这里上传不做全地址处理了
            if (this.maxCount === 1) {
              this.currentValue = res.url
            } else {
              this.currentValue.push(res.url)
            }
            this.$emit('input', this.currentValue)
          } else {
            file.status = 'failed'
            file.message = '上传失败'
            this.$toast.fail(res.error)
          }
        })
        .catch(() => {
          file.status = 'failed'
          file.message = '上传失败'
        })
    },

    deleteFile(file) {
      if (this.maxCount === 1) {
        this.currentValue = ''
      } else {
        this.currentValue.splice(
          this.currentValue.findIndex((x) => x === file.url),
          1
        )
      }
      this.$emit('input', this.currentValue)
    },
  },
}
</script>

至于另外别的 API,此处不再做多余的赘述,仅仅搬运下即可。

组件使用

  • 单个使用
javascript 复制代码
<ImageUploader v-model="imgUrl"></ImageUploader>

data() {
  return {
    imgUrl: ''
  }
},
  • 多个使用
ruby 复制代码
<ImageUploader v-model="imgList" :max-count="5"></ImageUploader>

data() {
  return {
    imgList: []
  }
},
相关推荐
卸任5 小时前
Vibe coding工具使用心得
前端·ai编程
brien95 小时前
为什么模型标称 1M,Google Antigravity 聊到 256K 却突然失忆?我们给它做了个原生行内监控圆环
前端
修炼的dance6 小时前
录屏关键帧间隔多少合适?六档实测,2秒到10秒小了75.6%
前端
Lank_M6 小时前
关键帧从1秒拉到10秒,录屏小87.3%,摄像头小10.8%
前端
补码缩写6 小时前
用 ffprobe 拆录屏,八成字节在 29 个关键帧上
前端
百思不得小李6 小时前
一个复杂的动态表单是如何实现的
前端·设计模式
陈随易6 小时前
Cmder 停更 17 个月,2.0 支持 Windows Terminal
前端·后端·程序员
风骏时光牛马6 小时前
前沿视界
前端
kyriewen6 小时前
我扫了 814 个依赖,97 个还敢要 Node 10
前端·javascript·程序员
陈随易7 小时前
3000 万人在用的翻译插件,重写一遍就是一门生意
前端·后端·程序员