• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录

我的博客我做主

  • 博客园
  • 联系
  • 订阅
  • 管理

公告

View Post

Vue3 文件上传组件

基于 vue3 element-plus 文件上传组件封装,兄弟姐妹们,将就看一下吧:

支持:粘贴文件路径上传、文件识别开关,上代码,FileUpload.vue

<template>
  <div>
    <div v-if="canRecognition">
      <el-checkbox v-model="isRecognition">开启识别(仅支持 .jpg .png .jpeg .bmp文件格式)</el-checkbox>
    </div>
    <el-input style="width:425px"
      clearable 
      @paste.native="onPasteUpload($event)" 
      placeholder="可在此处粘贴(ctrl+v / command+v)文件内容,或选择上传文件">
    </el-input>
    <div>
      <el-upload 
        style="width:100%;margin-top: 5px;"
        :limit="limit" 
        :multiple="!(canRecognition && isRecognition)"
        :before-upload="handleBeforeUpload"
        :on-preview="handlePictureCardPreview" 
        :action="defaultAction" 
        :http-request="uploadImg" 
        :on-remove="handleRemove" 
        :on-exceed="handleExceed"
        :accept="canRecognition && isRecognition ? '.jpg,.jpeg,.png,.bmp' : '*'"
        v-model:file-list="fileList">
          <el-button type="primary" plain>选择上传文件</el-button>
          <template #tip>
            <div class="el-upload__tip" v-if="showTip">
              请上传
              <template v-if="fileSize">
                大小不超过 <b style="color: #f56c6c">{{ fileSize }}MB</b>
              </template>
              <template v-if="fileType">
                格式为 <b style="color: #f56c6c">{{ fileType.join("/") }}</b>
              </template>
              的文件
            </div>
          </template>
      </el-upload>
    </div>
  </div>
</template>

<script setup name="FileUpload">
import { uploadFileSignleUrl, uploadFile } from '@/api/upload.js'
import { UUID } from 'uuidjs'
import remove from 'lodash/remove'
const { proxy } = getCurrentInstance();
const emits = defineEmits(['update:modelValue','recognition','removeCallback','successCallback']);
const props = defineProps({
  canRecognition:{
    type: Boolean,
    default: false,
  },
  recognizeAction:{ // 识别文件 的url
    type: String,
    default: '',
  },
  defaultAction: {
    type: String,
    default: uploadFileSignleUrl,
  },
  modelValue: [String, Object, Array],
  // 图片数量限制,默认不限制
  limit: {
    type: Number,
    default: 0, 
  },
  // 大小限制(MB)
  fileSize: {
    type: Number,
    default: 5,
  },
  // 文件类型, 例如['png', 'jpg', 'jpeg']
  fileType: {
    type: Array,
    default: () => ["doc","docx", "xls", "xlsx","ppt", "txt", "pdf","png", "jpg", "jpeg","bmp"],
  },
  // 是否显示提示
  isShowTip: {
    type: Boolean,
    default: true
  },
});
// 是否显示上传提示
const showTip = computed(
  () => props.isShowTip && (props.fileType || props.fileSize)
);
// 上传的文件
const fileList = computed({
  get: () => props.modelValue,
  set: (val) => {
    emits('update:modelValue', val)
  }
})
// 使用图片识别
const isRecognition = ref(true)
// 粘贴回调
const onPasteUpload = (event) => {
    let items = event.clipboardData && event.clipboardData.items;
    let file = null
    if (items && items.length) {
        // 检索剪切板items
        if (items[0].kind == 'file') {
            file = items[0].getAsFile()
        } else {
            proxy.$modal.msgError('粘贴内容不是文件内容,请重新复制后粘贴')
        }
    }
    if (!file) {
        return;
    }
    const uid = UUID.generate()
    file.uid = uid
    fileList.value.push({ 
      name: file.name,
      size: file.size,
      filename: 'file', 
      raw: file,
      status: 'ready',
      uid:uid,
    })
    uploadImg({ filename: 'file', file: file })
}
const getFormData = (filename,file) =>{
    const formData = new FormData();
    // const name = file.name.replace(/\.[a-zA-Z]+$/g,'') + '_'+ new Date().getTime() // 带上时间戳,避免文件地址重复被覆盖
    // formData.append('name', name);
    formData.append(filename, file);
    return formData
}
// 文件上传
const uploadImg = (file)=>{
    proxy.$modal.loading("上传中")
    const data = getFormData(file.filename,file.file)
    const url = (props.canRecognition && isRecognition.value) ? props.recognizeAction : props.defaultAction
    uploadFile(url, data).then(res=>{
        if(res.code == 200){
          if(res.data){
            // 文件url
            let current = fileList.value.find(z=>z.uid == file.file.uid)
            current.status = 'success'
            const fileData = props.canRecognition && isRecognition.value ? res.data.oss : res.data
            current.url = fileData.url
            current.ossId = fileData.ossId
            current.fileName = fileData.fileName
            // 如果开启文件识别,返回识别内容
            if(props.canRecognition && isRecognition.value){
              emits("recognition", { 
                fileName: current.fileName, 
                fileUrl: current.url, 
                recognitionData : (res.data.recognition?.data || res.data.recognition)
              })
            }
          }
          emits('successCallback')
        }else{
            remove(fileList.value, z=>z.uid == file.file.uid)
            proxy.$modal.msgError(res.msg || '发生错误,请稍后重试!')
        }
        proxy.$modal.closeLoading()
    }).catch(error=>{
        remove(fileList.value, z=>z.uid == file.file.uid)
        proxy.$modal.closeLoading()
        proxy.$modal.msgError(
            error && typeof error == 'object'
            ? error.statusText || error.message || JSON.stringify(error)
            : error || '发生错误,请稍后重试!'
        )
    })
}
// 上传前校验
const handleBeforeUpload = (file)=>{
  if (props.fileType.length) {
      const fileName = file.name.split('.');
      const fileExt = fileName[fileName.length - 1];
      const isTypeOk = props.fileType.indexOf(fileExt) >= 0;
      if (!isTypeOk) {
        proxy.$modal.msgError(`文件格式不正确, 请上传${props.fileType.join("/")}格式文件!`);
        return false;
      }
    }
    if (props.fileSize) {
      const isLt = file.size / 1024 / 1024 < props.fileSize;
      if (!isLt) {
        proxy.$modal.msgError(`上传文件大小不能超过 ${props.fileSize} MB!`);
        return false;
      }
    }
    return true
}
// 文件超出限制
const handleExceed = ()=>{
    proxy.$modal.msgError(`上传文件数量不能超过 ${props.limit} 个!`);
}
// 预览文件
const handlePictureCardPreview = (uploadFile)=>{
    window.open(uploadFile.url)
}
// 文件删除回调
const handleRemove = (file) => {
  emits("removeCallback",file)
}
// 格式转化 url,url,url =>Array<{ name , url}>
const fileUrlStringToList =(fileStr, name)=>{
    if(fileStr && fileStr.length >0){
        return fileStr.split(',').map((url,index)=>{
            return { name : name ? (name + (index + 1) + url.split('.').pop()) : url.split('/').pop(), url : url } 
        })
    }else{
        return []
    }
}
// 格式转换 Array<{ name , url, ...attrs}> => url,url,url
const fileListToUrlString = (fileList)=>{
    if(fileList && fileList.length >0){
        return (fileList||[]).map(z=>z.url).join(',')
    }else{
        return ''
    }
}
// 仅保留 name 和 url Array<{ name , url, ...attrs}> => Array<{ name , url }>
const stringifyForFileNameAndUrl = (fileList)=>{
    if(fileList && fileList.length >0){
        return JSON.stringify((fileList||[]).map(z=>{return { url : z.url, name : z.name}}))
    }else{
        return ''
    }
}
// 解析 数组字符串
const parseFileList = (fileList)=>{
    if(fileList && fileList.length >0){
        return JSON.parse(fileList)
    }else{
        return []
    }
}
defineExpose({
  fileUrlStringToList,
  fileListToUrlString,
  stringifyForFileNameAndUrl,
  parseFileList
})
</script>

<style scoped lang="scss">
</style>


作者:胡倩倩0903
出处:https://www.cnblogs.com/kitty-blog/
本文版权归作者和博客园共有,欢迎转载,但必须给出原文链接,并保留此段声明,否则保留追究法律责任的权利。

posted on 2023-05-08 14:44  kitty20180903suzhou  阅读(431)  评论(0)    收藏  举报

刷新页面返回顶部
 
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3