拖拽文件上传组件

// 组件引用界面
<template>
    <div>
        <FileUpload ref="fileUploadRef" :fileId="ruleForm.fileId" :fileName="ruleForm.fileName" accept=".xlsx,.xls,.pdf,.doc,.docx,.jpg,.png" :max-size="50" tip-text="最大上传50MB" disabled />
    </div>
</template>


<script setup>
import { ref, onMounted } from 'vue'
import FileUpload from '@workspace/components/fileUpload.vue'
const ruleForm = ref({
    fileId:'',
    fileName:''
})
</script>
//组件界面
<template>
  <div class="file-upload-container">
    <el-upload
      class="upload-dragger"
      drag
      action="#"
      :auto-upload="false"
      :accept="accept"
      :on-change="handleFileChange"
      :on-remove="handleFileRemove"
      :file-list="fileList"
      :disabled="loading || disabled"
    >
      <i class="el-icon-upload" />
      <div class="el-upload__text">拖拽文件到此处,或<em>点击上传</em></div>
      <template #tip>
        <div class="el-upload__tip">
          <span v-if="!disabled">{{ tipText }}</span>
          <span v-if="currentFile" style="color: #409eff; cursor: pointer; margin-left: 10px" @click="downloadFile"> 下载当前文件 </span>
        </div>
      </template>
    </el-upload>

    <!-- 加载状态 -->
    <el-text v-if="loading" style="margin-left: 10px; color: #666">
      <el-icon><Loading /></el-icon>
      文件处理中...
    </el-text>
  </div>
</template>

<script setup>
import { ref, watch, defineProps, defineExpose, nextTick } from 'vue'
import { ElMessage } from 'element-plus'
import { downFile } from '@workspace/api/driver.ts' // 换成自己项目的

// 组件配置 Props
const props = defineProps({
  // 文件ID,用于回显
  fileId: { type: [String, Number], default: '' },
  // 文件名
  fileName: { type: String, default: '' },
  // 文件类型,例如:.xlsx,.xls,.pdf,.doc,.docx
  accept: { type: String, default: '' },
  // 是否禁用
  disabled: { type: Boolean, default: false },
  // 文件大小限制,单位 MB
  maxSize: { type: Number, default: 10 },
  // 文件类型提示
  tipText: { type: String, default: '支持所有格式文件,大小不超过 10MB' }
})

// 响应式数据
const fileList = ref([])
const loading = ref(false)
// 当前文件对象(包含raw/name/size/fileId)
const currentFile = ref(null)

// ===================== 核心:文件选择 =====================
const handleFileChange = (file) => {
  console.log(file)
  clearAll()
  const rawFile = file.raw

  // 大小校验
  const maxSizeByte = props.maxSize * 1024 * 1024
  if (rawFile.size > maxSizeByte) {
    ElMessage.error(`文件大小不能超过 ${props.maxSize}MB!`)
    return
  }

  // 赋值
  fileList.value = [file]
  currentFile.value = {
    ...file,
    raw: rawFile,
    name: file.name,
    size: rawFile.size
  }
  ElMessage.success('文件选择成功!')
}

// ===================== 移除文件 =====================
const handleFileRemove = () => {
  clearAll()
  ElMessage.success('文件已移除')
}

// ===================== 下载文件 =====================
const downloadFile = () => {
  if (!currentFile.value) return

  // 本地文件直接下载
  if (currentFile.value.raw) {
    const blob = currentFile.value.raw
    const url = URL.createObjectURL(blob)
    const a = document.createElement('a')
    a.href = url
    a.download = currentFile.value.name
    a.click()
    URL.revokeObjectURL(url)
    ElMessage.success('文件下载成功')
    return
  }

  // 通过 fileId 下载(接口)
  if (props.fileId) {
    downloadFileById(props.fileId, currentFile.value.name)
  }
}

// 接口下载文件
const downloadFileById = async (fileId, fileName) => {
  try {
    loading.value = true
    const res = await downFile(fileId)
    const blob = new Blob([res.data])

    const url = URL.createObjectURL(blob)
    const a = document.createElement('a')
    a.href = url
    a.download = fileName || `文件_${fileId}`
    a.click()
    URL.revokeObjectURL(url)

    ElMessage.success('文件下载成功')
  } catch (err) {
    console.error('下载失败:', err)
    ElMessage.error('文件下载失败')
  } finally {
    loading.value = false
  }
}

// ===================== 清空数据 =====================
const clearAll = () => {
  fileList.value = []
  currentFile.value = null
}

// ===================== 监听 fileId 自动回显文件 =====================
watch(
  () => [props.fileId, props.fileName],
  async ([newId, newName]) => {
    if (newId) {
      loading.value = true
      try {
        // 仅构造文件对象,不读取内容(无预览)
        const mockFile = {
          name: newName || `文件_${newId}`,
          status: 'success',
          fileId: newId
        }
        fileList.value = [mockFile]
        currentFile.value = mockFile
      } catch (err) {
        console.error('文件回显失败:', err)
        clearAll()
      } finally {
        loading.value = false
      }
    } else {
      nextTick(() => clearAll())
    }
  },
  { immediate: true }
)

// 暴露给父组件使用
defineExpose({
  currentFile, // 当前文件对象
  fileList, // 文件列表
  clearAll, // 清空方法
  downloadFile // 下载方法
})
</script>

<style scoped>
.file-upload-container {
  width: 100%;
}
.upload-dragger {
  --el-upload-dragger-bg-color: #fafafa;
}
:deep(.el-mp-upload.is-disabled) {
  display: none !important;
}
</style>

<style>
/* 全局样式调整上传区域 */
.el-upload--disabled .el-upload__text {
  color: #c0c4cc !important;
}
</style>

拖拽和点击上传都可实现

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值