antd-design-vue 图片上传组件封装

585 阅读1分钟

antd-design-vue的upload组件上传的使用并不是很友好,需要需要定义一个数组对象来展示图片,二次封装,可以使得仅需要使用少量代码就可以调用,配合之前封装好的图片预览插件juejin.cn/post/743178… ,非常完美

使用方法:

import UploadImg from '@/components/upload/UploadImg.vue'

<UploadImg v-model="form.imgList"></UploadImg>

1:props 参数

继承antd上传组件所有参数,扩展自定义参数

属性描述默认
v-modelAarry[string]初始化的值 格式为:必须为字符串数组 如:['httpxxx', 'httpxxx']
fromatAarry[string]格式 默认['.png', '.jpg', '.gif', '.jpeg', '.apng']
sizenumber图片大小(M) 默认10M

2:事件回调函数

属性描述默认
upload-success上传图片成功(file, imgList)=> {}
upload-error上传图片失败(file, imgList)=> {}
<template>
  <a-upload-dragger
    v-bind="$attrs"
    :file-list="fileList"
    name="file"
    @change="handleChange"
    :accept="format.join(',')"
    list-type="picture"
    @preview="handlePreview"
    :beforeUpload="handleBeforeUpload"
    @remove="handleRemove"
  >
    <div class="ant-upload">
      <div>
        <UploadOutlined style="font-size: 32px" />
        <div>点击或者拖拽导入</div>
        <div>格式为{{ format.join('、') }},单张不超过{{ size }}M</div>
      </div>
    </div>
  </a-upload-dragger>
</template>
<script setup>
/**
图片上传组件

使用方法:
import PoUploadImg from '@/poComponents/upload/PoUploadImg.vue'

<PoUploadImg v-model="form.imgList"></PoUploadImg>


1:props 参数

继承antd上传组件所有参数,扩展自定义参数
v-model   Aarry[string]     初始化的值     格式为:必须为字符串数组 如:['httpxxx', 'httpxxx']
fromat    Aarry[string]     格式          默认['.png', '.jpg', '.gif', '.jpeg', '.apng']
size      number            图片大小(M)   默认10M

2:事件回调函数

upload-success   上传图片成功        (file, imgList)=> {}   
upload-error     上传图片失败        (file, imgList)=> {}


 */
import { UploadOutlined } from '@ant-design/icons-vue'
import { UploadDragger as AUploadDragger, message } from 'ant-design-vue'
import { ref, useAttrs, watch } from 'vue'
import usePreviewImg from './usePreviewImage'
import { couponUpload } from '@/service/api'
import errorImg from '@/assets/icon/error.png'

const attr = useAttrs()
const previewImage = usePreviewImg()
const props = defineProps({
  // 格式
  format: {
    type: Array,
    default() {
      return ['.png', '.jpg', '.gif', '.jpeg', '.apng']
    }
  },
  // 大小,10M
  size: {
    type: Number,
    default: 10
  },
  // 传入的值,必须为字符串数组 如:['httpxxx', 'httpxxx']
  modelValue: {
    type: Array,
    required: true,
    default() {
      return []
    }
  }
})
const emit = defineEmits(['update:modelValue', 'upload-success', 'upload-error'])
const fileList = ref([
  // {
  //   status: 'uploading',
  //   thumbUrl: 'https://xxx',
  //   uid: 'vc-upload-1730864018078-6',
  //   url: 'xxx.png'
  // }
])

watch(
  () => props.modelValue,
  (value) => {
    // 如果内容没有发生变化,则不更新内容
    let hasUplaod = false
    const oldValue = fileList.value.map((item) => {
      if (item.status === 'done') {
        return item.url
      } else if (item.status === 'uploading' || item.status === 'error') {
        hasUplaod = true
      }
    })
    // 如果url数量没有变化或者有正在上传的图片或者出错,不更新fileList,只有在父组件传值进来的时候,才会改变
    if (value.join(',') === oldValue.join(',') || hasUplaod) return

    // 需要清空内部的值
    fileList.value = []
    value.forEach((val, index) => {
      // 校验
      if (typeof val !== 'string') {
        throw new Error('图片上传组件:v-model的数据必须为字符串数组!')
      }

      const item = {
        thumbUrl: val,
        url: val,
        status: 'done',
        uid: 'uid' + index
      }
      fileList.value.push(item)
    })
  },
  { immediate: true }
)

const emitDate = () => {
  const list = []
  fileList.value.forEach((item) => {
    if (item.status === 'done') {
      list.push(item.url)
    }
  })

  return list
}
// 自己接管上传的处理
const handleBeforeUpload = () => {
  return false
}
// 图片移除
const handleRemove = (file) => {
  const index = fileList.value.findIndex((item) => item.uid === file.uid)
  fileList.value.splice(index, 1)

  emit('update:modelValue', emitDate())
}
// 内容发生改变
const handleChange = (info) => {
  const file = info.file
  const status = info.file.status

  if (file.size > 1024 * 1024 * props.size) {
    message.error(`文件大小不能超过${props.size}M`)
    return
  } else if (attr.maxCount && fileList.value.length >= attr.maxCount) {
    message.error(`图片数量不能超过${attr.maxCount}张`)
    return
  }

  if (!status) {
    imgageUploadFn(file)
  }
}
// 上传图片函数
const imgageUploadFn = (file) => {
  const item = {
    uid: file.uid,
    status: 'uploading'
  }
  fileList.value.unshift(item)

  const formData = new FormData()
  formData.append('file', file)

  // 上传接口调用
  couponUpload(formData)
    .then((res) => {
      const item = fileList.value.find((sub) => sub.uid === file.uid)

      item.thumbUrl = res
      item.url = res
      item.status = 'done'

      const list = emitDate()

      emit('update:modelValue', list)
      emit('upload-success', file, list)
    })
    .catch(() => {
      const item = fileList.value.find((sub) => sub.uid === file.uid)
      item.thumbUrl = errorImg
      item.url = errorImg
      item.status = 'error'

      emit('upload-error', file, emitDate())
    })
}
// 预览
const handlePreview = (file) => {
  const index = fileList.value.findIndex((item) => item.uid === file.uid)
  previewImage.show({
    list: fileList.value.map((sub) => sub.thumbUrl),
    current: index
  })
}
</script>
<style lang="less" scoped>
.ant-upload {
  height: 118px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #286043;
}
</style>