封装el-upload组件,用于上传图片和视频的组件

使用环境 vue3+element plus
需要根据后端返回结构修改的函数:onPreview onRemove onSuccess

组件使用

基本使用

在这里插入图片描述
源代码:

<script setup>
import AutoUploadFile from '@/components/auto-upload-file/index.vue'
function change(urls){
  console.log(urls)
}
</script>

<template>
  <AutoUploadFile @change="change"/>
</template>

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

初始文件列表回显

在这里插入图片描述
源代码:

<script setup>
import AutoUploadFile from '@/components/auto-upload-file/index.vue'
import {ref} from "vue";

const initUrls = ref([
  'http://127.0.0.1:9090/file/local-plus/6700f235df13a72064bf9167.png'
])

function change(urls) {
  console.log(urls)
}
</script>

<template>
  <AutoUploadFile :init-file-urls="initUrls" accept=".jpg,.jpeg,.png" @change="change">
  </AutoUploadFile>
</template>

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

定义上传格式

如果有mp4类型,文件展示列表就会变为text

在这里插入图片描述
源代码:

<script setup>
import AutoUploadFile from '@/components/auto-upload-file/index.vue'
function change(urls){
  console.log(urls)
}
</script>

<template>
  <AutoUploadFile accept=".jpg,.jpeg,.png,.mp4" :max-size="100" @change="change"/>
</template>

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

自定义上传按钮样式

在这里插入图片描述
源代码:

<script setup>
import AutoUploadFile from '@/components/auto-upload-file/index.vue'
function change(urls){
  console.log(urls)
}
</script>

<template>
  <AutoUploadFile accept=".jpg,.jpeg,.png,.mp4" :max-size="100" @change="change">
    <el-button>自定义上传样式</el-button>
  </AutoUploadFile>
</template>

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

组件属性

属性名属性类型默认值是否必填说明
initFileUrlsArray[]用于上传列表的回显,接收外部初始化url数组
listTypeStringpicture-card上传列表展示格式。可选项:picture-card/picture/text , 但是如果accept允许mp4,那么listType就会自动转化为text
actionString#上传文件时的接口服务
headersObject{}请求头
nameStringfile提交文件时的字段名
withCredentialsBooleantrue是否支持cookie凭证信息
showFileListBooleantrue是否展示上传列表
acceptString“.jpg,.jpeg,.png”可以上传的文件类型
limitNumber5允许上传的最大文件数量
maxSizeNumber5最大文件大小,单位MB
tipPrefixString“”提示信息前缀
showTipBooleantrue是否展示提示信息
showOverflowTooltipBooleantruetip过长是否使用省略号显示
multipleBooleantrue可以多选文件
autoUploadBooleantrue默认自动上传
sizeString100pxpicture-card的尺寸大小

组件事件

事件名事件参数列表说明
onProgress(e,file,fileList)用于监控文件上传进度
change(urls)上传列表改变时的回调

组件暴露方法

方法名参数列表说明
clearFiles()清空文件列表
submit()用于手动提交

组件插槽

插槽名插槽回显参数说明
default上传文件时的点击区域,用于自定义样式
file{ file }文件列表样式,用于自定义缩略图
trigger用于手动提交,只选择文件,不发起提交请求的插槽

组件源码:(auto-upload-file.vue)

<!--需要根据后端返回结构修改的函数:onPreview onRemove onSuccess -->
<script setup>
import {computed, onMounted, onUpdated, ref} from "vue";
import {ElMessage} from "element-plus";

const prop = defineProps({
  // 文件列表
  initFileUrls: {
    type: Array,
    default: []
  },
  // 展示格式
  listType: {
    type: String,
    default: 'picture-card'
  },
  // 上传文件的默认接口
  action: {
    type: String,
    default: 'http://localhost:9090/upload/file'
  },
  // 请求头(添加token等)
  headers: {
    type: Object,
    default: {}
  },
  // 上传时的文件名(需要与后端接收时的字段保持一致)
  name: {
    type: String,
    default: 'file'
  },
  // 默认支持cookie凭证信息
  withCredentials: {
    type: Boolean,
    default: true
  },
  // 默认展示上传文件列表
  showFileList: {
    type: Boolean,
    default: true
  },
  // 可接受文件的类型
  accept: {
    type: String,
    default: '.jpg,.jpeg,.png'
  },
  // 允许上传的最大文件数量
  limit: {
    type: Number,
    default: 5
  },
  // 单位MB
  maxSize: {
    type: Number,
    default: 5
  },
  // 提示前缀
  tipPrefix: {
    type: String,
    default: ''
  },
  // 是否展示提示
  showTip: {
    type: Boolean,
    default: true
  },
  // tip过长使用省略号显示
  showOverflowTooltip: {
    type: Boolean,
    default: true
  },
  // 可以多选文件
  multiple: {
    type: Boolean,
    default: true
  },
  // 默认自动上传
  autoUpload: {
    type: Boolean,
    default: true
  },
  // picture-card尺寸大小
  size: {
    type: String,
    default: '6.25rem'
  }
})

const emit = defineEmits(['onProgress', 'change'])
defineExpose({
  clearFiles,
  submit
})

// el-upload使用的文件列表
const fileList = ref(prop.initFileUrls.map(item => {
  return {
    name: getFileName(item),
    url: item
  }
}))

const uploadRef = ref()

// 存放后端返回的url,及初始化url
const urls = ref([...prop.initFileUrls])

// 如果允许上传视频,则默认只能使用text显示上传列表
const listTypeCmp = computed(() => {
  return prop.accept.indexOf('mp4') !== -1 ? 'text' : prop.listType
})

// 提示信息
const tip = computed(() => {
  return `${prop.tipPrefix ? prop.tipPrefix + ',' : ''}文件类型:
  ${prop.accept.replaceAll(',', '/').replaceAll('.', '')}
  ,文件大小不能超过:${prop.maxSize}MB`
})

// 文件上传之前的钩子
function beforeUpload(e) {
  const MB = e.size / (1024 * 1024)
  if (MB > prop.maxSize) {
    ElMessage.error(`文件的大小不能超过:${prop.maxSize}MB`)
    return false
  }
}

// 上传成功的回调(根据后端返回值不同需要略作修改)
function onSuccess(e, file) {
  urls.value.push(e)
  emit('change', urls.value)
}

const dialogFileUrl = ref()
const dialogVisible = ref(false)

// 预览图片(根据后端返回值不同需要略作修改)
function onPreview(e) {
  dialogFileUrl.value = e.response || e.url
  dialogVisible.value = true
}

// 移除文件(根据后端返回值不同需要略作修改)
function onRemove(e) {
  urls.value = urls.value.filter(item => item !== (e.response || e.url))
  emit('change', urls.value)
}

// 超出最大文件限制时,执行的钩子函数
function onExceed(e) {
  ElMessage.error(`超出要求的文件最大数量:${prop.limit}`)
}

// 文件上传失败时的回调
function onError() {
  ElMessage.error('文件上传失败')
}

// 上传进度回调
function onProgress(e, file, fileList) {
  emit('onProgress', e, file, fileList)
}

// 清空文件列表
function clearFiles() {
  uploadRef.value.clearFiles()
  urls.value = []
  emit('change', urls.value)
}

// 手动提交
function submit() {
  uploadRef.value.submit()
}

// 获取后缀
function getSuffix(url) {
  return url.substring(url.lastIndexOf('.') + 1)
}

// 获取文件名
function getFileName(url) {
  return url.substring(url.lastIndexOf('/') + 1)
}

// 阻止点击tip时触发的上传行为
function preventClick(event) {
  event.stopPropagation()
}

// 初始化picture-card大小
function initSize() {
  const uploadListItem = document.querySelector('.el-upload-list--picture-card')
  const uploadPictureCard = document.querySelector('.el-upload--picture-card')
  if (uploadListItem) {
    uploadListItem.style.setProperty('--el-upload-list-picture-card-size', prop.size)
  }
  if (uploadPictureCard) {
    uploadPictureCard.style.setProperty('--el-upload-picture-card-size', prop.size)
    if (listTypeCmp.value==='picture-card'){
      uploadPictureCard.style['margin-bottom']='1.56rem'
    }
  }
}

// 动态处理展示样式
function handleStyle() {
  initSize()
}

onUpdated(() => {
  handleStyle()
})
onMounted(() => {
  handleStyle()
})

</script>

<template>
  <el-upload
      ref="uploadRef"
      class="upload-box"
      v-model:file-list="fileList"
      :list-type="listTypeCmp"
      :action="action"
      :headers="headers"
      :with-credentials="withCredentials"
      :name="name"
      :show-file-list="showFileList"
      :before-upload="beforeUpload"
      :on-success="onSuccess"
      :on-remove="onRemove"
      :on-preview="onPreview"
      :accept="accept"
      :limit="limit"
      :on-exceed="onExceed"
      :on-error="onError"
      :on-progress="onProgress"
      :auto-upload="autoUpload"
      :multiple="multiple"
  >
    <template #default v-if="autoUpload">
      <div class="upload">
        <div class="upload-default">
          <slot>
            <el-icon v-if="listTypeCmp==='picture-card'"
                     style="width: 100%;height: 100%;font-size: 1.88rem;color: #888888">
              <Plus/>
            </el-icon>
            <el-button v-else>上传文件</el-button>
          </slot>
        </div>

        <div v-if="listTypeCmp==='picture-card' && showTip" class="upload-tip abs-tip" @click="preventClick">
          <div class="tip-div" :title="tip"
               :class="{'text-overflow-ellipsis':showOverflowTooltip}">
            <span>{{ tip }}</span>
          </div>
        </div>
      </div>
    </template>
    <template #tip v-if="listTypeCmp!=='picture-card' && showTip">
      <div class="upload-tip" @click="preventClick">
        <div class="tip-div" :title="tip"
             :class="{'text-overflow-ellipsis':showOverflowTooltip}">
          <span>{{ tip }}</span>
        </div>
      </div>
    </template>
    <!--    自定义缩略图-->
    <template #file="{file}">
      <slot name="file" :file="file"></slot>
    </template>
    <template #trigger v-if="!autoUpload">
      <slot name="trigger"></slot>
    </template>
  </el-upload>
  <!--文件预览-->
  <el-dialog v-model="dialogVisible" width="80%">
    <video style="height: 100%;width: 100%" v-if="getSuffix(dialogFileUrl)==='mp4'" :src="dialogFileUrl" controls/>
    <el-image v-else style="height: 80vh" w-full :src="dialogFileUrl" alt="Preview Image"/>
  </el-dialog>
</template>

<style lang="scss" scoped>

.upload-box {
  box-sizing: border-box;

  .upload {
    position: relative;
    height: 100%;
    width: 100%;
    display: flex;
    justify-content: center;
    align-items: center;
    .abs-tip{
      position: absolute;
      bottom: -1.88rem;
      left: 0;
    }
  }

  .upload-tip {
    width: 100%;

    .tip-div {
      width: 100%;
      cursor: pointer;
      color: red;
      font-weight: 200;
      font-size: 0.75rem;
    }

    .text-overflow-ellipsis {
      display: inline-block;
      overflow: hidden;
      white-space: nowrap; /* 不换行 */
      text-overflow: ellipsis; /* 超出部分显示为省略号 */
    }
  }
}
</style>

<think>嗯,用户想用el-upload同时上传图片视频,我得想想怎么实现。首先,我需要回忆一下Element UI的el-upload组件的基本用法。根据用户提供的引用内容,他们之前已经了解过手动上传、覆盖默认上传行为的方法,比如使用http-request或者auto-upload设置为false的情况。 用户的问题是需要同时上传图片视频,可能需要处理不同的文件类型。首先,应该设置accept属性来限制用户只能选择图片视频文件。但是,accept属性可能需要同时包含图片视频的MIME类型,比如image/*video/*。不过,这样可能会让用户选择所有类型的图片视频,可能需要更具体的类型,比如image/png, image/jpeg, video/mp4等。 接下来,上传前的校验。在before-upload钩子中,可以检查文件的类型,是否符合图片视频的格式。比如,如果文件类型不在允许的列表中,就提示用户,并阻止上传。这里可能需要获取文件的type属性,然后判断是否属于image或video下的子类型。 然后,上传的实现。用户可能需要手动上传,也就是选择文件后不自动上传,而是自己处理。这时候需要设置auto-upload为false,然后在某个事件(比如点击按钮)触发时,将文件列表中的文件逐个上传。或者,使用http-request覆盖默认的上传行为,这样可以自定义上传逻辑,比如添加其他参数,分批次处理等。 另外,用户可能需要同时上传多个文件,包括图片视频,所以需要处理多个文件的并发上传。可能需要使用Promise.all来同时处理多个上传请求,或者逐个上传。同时,在上传过程中需要显示上传状态,比如进度条,成功或失败的提示。 还有,可能需要限制上传文件的大小,比如图片不超过2MB,视频不超过50MB。这也可以在before-upload中进行校验,如果文件大小超过限制,则提示用户并取消上传。 界面展示方面,可能需要显示已上传图片视频的预览。对于图片,可以使用缩略图;对于视频,可能需要显示视频的封面或者默认图标,并允许用户点击播放。此外,上传成功后可能需要隐藏上传按钮,或者限制上传的数量,比如每个类型只能上传一个文件,或者总共上传不超过一定数量。 根据引用中的示例,用户可能已经知道如何手动处理文件列表,比如在on-change事件中更新fileList。这时候,可以维护一个数组,保存所有选择的文件,并在上传时遍历这个数组,分别处理图片视频上传请求。 还有可能需要区分图片视频,分别上传到不同的后端接口。这时候需要在http-request中根据文件类型调用不同的API。例如,图片上传到/image/upload视频上传到/video/upload,并在请求中添加相应的参数。 最后,测试示例代码是否正确,确保图片视频都能正确上传,并且处理各种异常情况,比如网络错误,文件类型错误,大小超限等。</think>以下是如何使用 el-upload 组件实现同时上传图片视频的解决方案: ### 一、实现步骤 1. **文件类型限制** 通过设置 `accept` 属性限定文件类型为图片视频: ```html <el-upload accept="image/*, video/*" :auto-upload="false" :on-change="handleFileChange" :file-list="fileList" > <el-button>选择文件</el-button> </el-upload> ``` 2. **上传前校验** 在 `before-upload` 钩子中校验文件类型大小: ```javascript methods: { beforeUpload(file) { const isMedia = file.type.startsWith('image/') || file.type.startsWith('video/'); const isLt50M = file.size / 1024 / 1024 < 50; if (!isMedia) this.$message.error('仅支持图片视频格式'); if (!isLt50M) this.$message.error('文件大小不能超过50MB'); return isMedia && isLt50M; } } ``` 3. **手动上传实现** 通过自定义 `http-request` 方法处理混合上传: ```html <el-upload :http-request="customUpload" :multiple="true" > <el-button>上传文件</el-button> </el-upload> ``` ### 二、完整示例代码 ```html <template> <div> <el-upload ref="mediaUpload" action="#" :auto-upload="false" :multiple="true" :file-list="fileList" :before-upload="beforeUpload" :on-change="handleFileChange" :http-request="customUpload" accept="image/*, video/*" > <el-button>选择文件</el-button> <template #tip> <div class="el-upload__tip">支持jpg/png/mp4格式,单个文件不超过50MB</div> </template> </el-upload> <el-button @click="submitUpload">立即上传</el-button> </div> </template> <script> export default { data() { return { fileList: [] }; }, methods: { beforeUpload(file) { const isMedia = file.type.startsWith('image/') || file.type.startsWith('video/'); const isLt50M = file.size / 1024 / 1024 < 50; if (!isMedia) this.$message.error('仅支持图片视频格式'); if (!isLt50M) this.$message.error('文件大小不能超过50MB'); return isMedia && isLt50M; }, handleFileChange(file, fileList) { this.fileList = fileList; }, async customUpload(param) { const formData = new FormData(); formData.append('file', param.file); formData.append('type', param.file.type.split('/')[0]); // 区分image/video try { const response = await this.$http.post('/api/upload', formData); param.onSuccess(response); } catch (error) { param.onError(error); } }, submitUpload() { this.$refs.mediaUpload.submit(); } } }; </script> ``` ### 三、关键特性说明 1. **混合文件处理** 通过 `accept="image/*, video/*"` 实现类型过滤,`multiple` 属性支持多选[^1][^2] 2. **分类上传策略** 在 `formData` 中添加类型标识字段,便于后端区分处理媒体类型[^3] 3. **进度反馈优化** 可添加 `on-progress` 事件实现上传进度条显示
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值