简单上传组件的封装

上传组件的封装

只有一部分,另一部分是在基础表单组件里只是个展示和上传没关系就不写了

<script>
export default {
  name: 'BaseUpload',
}
</script>
<template>
  <div>
    <div
      ref="uploadArea"
      :class="{ 'upload-area': true, 'drop-active': isDrop }"
      @dragover.prevent="dragover"
      @drop.stop.prevent="handleDrop"
      @click="openFileDialog"
      @dragleave="onDragleave"
    >
      <input
        type="file"
        ref="fileInput"
        hidden
        @change="handleFileChange"
        :multiple="multiple"
      />
      <slot name="default">
        <p>拖拽文件到这里,或点击选择文件</p>
      </slot>
    </div>
    <div class="file-list">
      <template v-for="(item, index) in fileList" :key="index">
        <div v-if="checkFileType(item.url)" class="file-list-block">
          <div class="file-list_close" @click="handleDelete(index)">
            <el-icon size="16"><CircleClose /></el-icon>
          </div>
          <ImagePreview class="file-preview" :src="item.url"></ImagePreview>
        </div>
        <template v-else>
          <el-tag
            class="file-tag"
            closable
            type="primary"
            @close="handleDelete(index)"
            @click="handleClick(item)"
          >
            {{ item.name }}
          </el-tag>
        </template>
      </template>
    </div>
  </div>
</template>

<script setup>
import { fileUpload } from '@/api/base/upload'
import ImagePreview from '@/components/ImagePreview'
const { proxy } = getCurrentInstance()
const emit = defineEmits(['upload-success', 'upload-error', 'upload-delete'])
const props = defineProps({
  fileType: {
    type: Array,
    default: () => [],
  },
  formProp: {
    type: String,
    default: '',
  },
  multiple: {
    type: Boolean,
    default: false,
  },
  fileList: {
    type: Array,
    default: () => [],
  },
})
let isDrop = ref(false)
let { fileType, formProp, fileList } = toRefs(props)
let imgType = ['png', 'jpeg', 'jpg', 'svg', 'gif']
const uploadArea = ref(null)
const fileInput = ref(null)

onMounted(() => {
  // 初始化时聚焦文件输入框以支持键盘触发
  fileInput.value.focus()
})

const openFileDialog = () => {
  fileInput.value.click()
}

const handleFileChange = (e) => {
  const files = e.target.files
  if (files && files.length > 0) {
    uploadFiles(files)
  }
  e.target.value = ''
}

const handleDrop = (e) => {
  isDrop.value = false
  const files = e.dataTransfer.files
  if (files && files.length > 0) {
    uploadFiles(files)
  }
}

const uploadFiles = async (files) => {
  let formData = new FormData()
  let flag = beforeUpload(files)
  if (!flag) {
    proxy.$modal.msgError(
      `文件格式不正确, 请上传${fileType.value.join('/')}格式文件!`
    )
    return
  }

  for (let file of files) {
    formData.append('file', file)
  }
  fileUpload(formData)
    .then((res) => {
      let { data } = res
      emit('upload-success', data, formProp.value) // 假设后端返回成功信息
    })
    .catch((err) => {
      proxy.$modal.msgError(`上传失败${err}`)
      emit('upload-error', formProp.value)
    })
}

let dragover = () => {
  isDrop.value = true
}
let onDragleave = () => {
  isDrop.value = false
}
// 删除文件
let handleDelete = (index) => {
  emit('upload-delete', index, formProp.value)
}
//预览文件
let handleClick = (item) => {
  let link = document.createElement('a')
  link.href = item.url
  link.download = item.name
  link.style.display = 'none'
  document.body.appendChild(link)
  link.textContent = item.name
  link.setAttribute('target', '_black')
  link.click()
  link.remove()
}
function checkFileType(file) {
  let fileExt = file.split('.')
  let fileType = fileExt[fileExt.length - 1]
  return imgType.includes(fileType)
}
function beforeUpload(files) {
  let flag = true
  if (files) {
    for (let file of files) {
      let fileName = file.name.split('.')
      const fileExt = fileName[fileName.length - 1]
      if (fileType.value.length && !fileType.value.includes(fileExt)) {
        flag = false
      }
    }
  }
  return flag
}
</script>

<style scoped>
.upload-area {
  border: 2px dashed #ccc;
  padding: 20px;
  text-align: center;
  cursor: pointer;
}
.drop-active {
  border-color: #0b6fee;
}
.file-list {
  display: flex;
  align-items: flex-start;
  justify-content: flex-start;
  margin-top: 10px;
  column-gap: 10px;
}
.file-tag {
  margin-bottom: 10px;
  cursor: pointer;
}
.file-preview {
  width: 60px;
  height: 60px;
}
.file-list-block {
  position: relative;
}
.file-list_close {
  position: absolute;
  top: -6px;
  right: 0;
  z-index: 1000;
}
</style>

效果图

在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

九亿少女无法触及的梦ى

您的赞赏,对我来说意义非凡

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值