Vue3 + Element Plus实现用户粘贴图片上传功能的组件

前言

本文将介绍如何使用 Vue 3 和 Element Plus 创建一个支持图片粘贴上传的组件,并提供详细的代码实现和说明。

组件功能概述

我们的目标是创建一个图片上传组件,具备以下功能:

  1. 支持用户通过拖拽或点击按钮选择图片上传。
  2. 支持用户直接粘贴图片到组件中进行上传。
  3. 显示上传图片的预览,并允许用户删除已上传的图片。
  4. 提供对上传文件的类型和大小检查。

代码实现

以下是实现上述功能的 Vue 3 组件代码:

<template>
  <div @paste="handlePaste">
    <el-upload
      v-model:file-list="fileList"
      action=""
      list-type="picture-card"
      :on-preview="handlePictureCardPreview"
      :before-remove="handleBeforeRemove"
      :before-upload="handleBeforeUpload"
      :http-request="handleUpload"
    >
      <el-icon><Plus /></el-icon>
    </el-upload>
    <el-dialog v-model:visible="dialogVisible">
      <img w-full :src="dialogImageUrl" alt="Preview Image" />
    </el-dialog>
    <span>
      Please note that the size of each image should not exceed 3MB, and we support files in JPG and PNG formats.
    </span>
  </div>
</template>

<script setup lang="ts">
import { Plus } from '@element-plus/icons-vue';
import type { UploadRawFile, UploadRequestOptions } from 'element-plus';
import { ElLoading } from 'element-plus';
import adminApi from '@/api/adminApi';
import showMessage from '~/utils/showMessage';

const fileList = ref<UploadRawFile[]>([]);
const fullPathList = ref<string[]>([]); // Full paths
const pathList = ref<string[]>([]); // Paths without domain

const dialogImageUrl = ref('');
const dialogVisible = ref(false);

const handleUpload = async (options: UploadRequestOptions): Promise<void> => {
  const { file } = options;
  const formData = new FormData();
  formData.append('file', file);

  const uploadLoading = ElLoading.service({
    lock: true,
    text: 'uploading...',
    background: 'rgba(0, 0, 0, 0.18)',
  });

  try {
    const { data } = await adminApi.uploadFile(formData);
    if (data) {
      fullPathList.value.push(data.url);
      pathList.value.push(data.path);
      emit('update:file-list', pathList.value);
      emit('update:full-path-list', fullPathList.value);
    }
  } catch (error) {
    showMessage({ message: 'Upload failed', type: 'error' });
  } finally {
    uploadLoading.close();
  }
};

const handleBeforeUpload = (file: File): boolean => {
  const isLt3M = file.size / 1024 / 1024 < 3;
  const isAcceptableType = ['image/jpeg', 'image/png'].includes(file.type);
  if (!isLt3M) {
    showMessage({ message: 'The size of each image should not exceed 3MB', type: 'error' });
    return false;
  }
  if (!isAcceptableType) {
    showMessage({ message: 'Only PNG and JPG formats are supported', type: 'error' });
    return false;
  }
  return true;
};

const handleBeforeRemove = (file: UploadRawFile): boolean => {
  const index = fileList.value.findIndex((item) => item.uid === file.uid);
  if (index !== -1) {
    fileList.value.splice(index, 1);
    pathList.value.splice(index, 1);
    fullPathList.value.splice(index, 1);
    emit('update:file-list', pathList.value);
    emit('update:full-path-list', fullPathList.value);
  }
  return true;
};

const handlePictureCardPreview = (file: UploadRawFile): void => {
  dialogImageUrl.value = file.url!;
  dialogVisible.value = true;
};

const handlePaste = (event: ClipboardEvent) => {
  const items = event.clipboardData?.items;
  if (!items) return;

  for (let i = 0; i < items.length; i++) {
    const item = items[i];
    if (item.kind === 'file' && item.type.startsWith('image/')) {
      const file = item.getAsFile();
      if (file) {
        const uploadFile: UploadRawFile = {
          ...file,
          uid: Date.now(), // 使用当前时间戳生成唯一的 uid
          status: 'success',
          url: URL.createObjectURL(file),
        };

        fileList.value.push(uploadFile);

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

        const uploadOptions: UploadRequestOptions = {
          file,
          onProgress: () => {},
          onSuccess: () => {},
          onError: () => {},
        };

        handleUpload(uploadOptions);
      }
    }
  }
};

const emit = defineEmits(['update:file-list', 'update:full-path-list']);

const clearFiles = () => {
  fileList.value = [];
  pathList.value = [];
  fullPathList.value = [];
  emit('update:file-list', []);
  emit('update:full-path-list', []);
};

defineExpose({ clearFiles });
</script>

<style scoped>
span {
  line-height: 20px;
  margin-top: 12px;
}
</style>

 

代码说明

  1. 组件结构

    • el-upload 组件用于处理图片的上传,支持图片预览和删除。
    • el-dialog 组件用于展示图片的详细预览。
  2. handleUpload 方法

    • 处理上传文件的逻辑,使用 FormData 将文件提交到服务器,并处理加载状态。
  3. handleBeforeUpload 方法

    • 在上传之前检查文件的大小和类型,确保符合要求。
  4. handleBeforeRemove 方法

    • 处理文件删除时的逻辑,更新文件列表。
  5. handlePictureCardPreview 方法

    • 显示图片的详细预览。
  6. handlePaste 方法

    • 处理粘贴事件,从剪贴板中提取图片文件并触发上传。
  7. 样式

    • 简单的样式调整,使组件界面更加美观。

使用 

<template>
      <el-form
        ref="handleFormRef"
        :model="handleForm"
        :rules="validateRules"
        label-width="100px"
        label-position="right"
      >
        <!-- 表单内容 -->
        <el-form-item prop="ImgPathList" label="" required>
          <FileUpload v-model:file-list="handleForm.ImgPathList" v-model:full-path-list="handleForm.imgFullPathList" ref="fileUploadRef" />
        </el-form-item>
      </el-form>
</template>

<script setup lang="ts">
  import FileUpload from '@/components/FileUpload/index.vue';
</script>

总结

通过以上代码,我们实现了一个功能完整的图片上传组件,支持用户通过点击、拖拽和粘贴方式上传图片。该组件能够处理图片的上传、预览、删除及类型和大小检查,为用户提供了更好的体验。希望这个示例能帮助你在项目中实现类似功能。

如果你有任何问题或需要进一步的帮助,欢迎在评论区留言!

评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值