vue3+ts - element-plus封装上传文件图片组件

  近期做到的项目中有涉及到上传图片上传文件的需求,因为是pc管理后台,用到了element-plus框架,所以我也一起使用element-plus中的上传图片上传图片功能,并对它进行封装成一个组件,方便在多个地方使用。

一、效果图

1、上传文件、视频

在这里插入图片描述

2、上传图片

在这里插入图片描述

二、代码分析及全部代码

  在这里上传图片和文件是分成了两个组件进行封装的,因为项目需求要求不一致,所以分开了,大家使用时有需要的话可以将它们合并到一起。

1、上传文件相关代码

<template>
  <div class="upload_wrap">
    <el-upload
      v-if="!props.isDisableUpload"
      class="upload"
      ref="uploadRef"
      :file-list="waitFileList"
      :multiple="props.isMultiple"
      :limit="props.limitNum"
      :accept="props.acceptType"
      :auto-upload="false"
      :show-file-list="false"
      :disabled="props.isDisableUpload"
      :on-change="handleChange"
    >
      <div class="el-upload__text">
        <img src="@/assets/images/icon_upload.png" />
        <span>上传文件</span>
      </div>
    </el-upload>
    <div class="template_list">
      <div class="template" v-for="(item, index) in waitFileList" :key="index">
        <span>
          <img src="@/assets/images/icon_link.png" />
        </span>
        <span class="documentName">{
    
    {
    
     item.name }}</span>
        <span v-if="!props.isDisableUpload">
          <el-icon color="#000000a6" size="16" @click="removeFile(item)"
            ><Close
          /></el-icon>
        </span>
        <span v-if="isDownLoad" style="paddingleft: 5px">
          <img
            src="@/assets/images/icon_download.png"
            @click="handleDownLoad(item)"
          />
        </span>
      </div>
    </div>
    <span class="tips" v-if="!props.isDisableUpload"
      >支持{
    
    {
    
     acceptTypeDesc }};文件大小不能超过{
    
    {
    
     props.maxFileSize }}M</span
    >
  </div>
</template>

<script lang="ts" setup>
import {
    
     ref, watch } from "vue";
import {
    
     ElLoading, ElMessage } from "element-plus";
import {
    
     request } from "@/api/axios";
import {
    
     Close } from "@element-plus/icons-vue";
const emits = defineEmits(["fileSuccess", "fileRemove"]);
interface Props {
    
    
  acceptType?: string; // 上传文件类型
  acceptTypeDesc?: string; // 描述 - 上传文件类型
  isMultiple?: boolean; //   是否可批量上传
  limitNum?: number; // 允许上传文件的最大数量
  isDisableUpload?: boolean; // 是否禁用上传
  maxFileSize?: number; // 文件大小
  action?: string;
  fileList?: any; // 回显的文件
  isDownLoad?: boolean; // 是否可以下载
}
// 接收父组件传递过来的参数
const props = withDefaults(defineProps<Props>(), {
    
    
  acceptType: ".xls,.doc",
  acceptTypeDesc: "doc/xls",
  isMultiple: true,
  limitNum: 10,
  isDisableUpload: false,
  maxFileSize: 10,
  action: "/activity/resource/uploadFile",
  fileList: [],
  isDownLoad: false,
});
let waitFileList = ref<any[]>([]);

waitFileList.value = props.fileList;
waitFileList.value?.forEach((item: any) => {
    
    
  item.name = item.original;
});

watch(
  () => props.fileList,
  () => {
    
    
    console.log("props.fileList====>", props.fileList);
    waitFileList.value = props.fileList;
    waitFileList.value?.forEach((item: any) => {
    
    
      item.name = item.original;
    });
  }
);

// 文件变化Handle 这里监听上传文件的变化是一个一个接收到变化的,所以文件也是一个一个上传到服务器上面的
const handleChange = async (file: any, fileList: any[]) => {
    
    
  // 防止多次执行change
  const rawFile = file.raw;
  const list = props.acceptTypeDesc.split("/");
  let acceptTypeList = list.map((its:string)=>{
    
    
    return getType(its)
  })
  // 如果要检索的字符串值没有出现,则该方法返回 -1
  const ars = acceptTypeList.filter((q:string)=>{
    
    
    return rawFile.type.indexOf(q)>-1
  })
  // 用于校验是否符合上传条件
  const type = props.acceptTypeDesc.replace("/", ", ");
  if (ars.length<1) {
    
    
    ElMessage.error(`仅支持格式为${
      
      type}的图片`);
    return false;
  } else if (rawFile.size / 1024 / 1024 > props.maxFileSize) {
    
    
    ElMessage.error(`文件大小不能超过${
      
      props.maxFileSize}MB!`);
    const arr = [...waitFileList.value];
    waitFileList.value = arr.filter((item: any) => {
    
    
      return item.uid != rawFile.uid;
    });
    return false;
  } else {
    
    
    let formData = new FormData();
    formData.append("file", rawFile);
    formData.append("fileType", "2");
    const loadingInstance = ElLoading.service({
    
    
      text: "正在上传",
      background: "rgba(0,0,0,.2)",
    });
    // 上传到服务器上面
    const requestURL: string = props.action;
    request("post", requestURL, formData, {
    
    
      headers: {
    
     "Content-Type": "multipart/form-data" },
    })
      .then(async (res: any) => {
    
    
        if (res.code == 0) {
    
    
          loadingInstance.close();
          let obj = {
    
    
            ...res.data,
            name: res.data.original,
          };
          emits("fileSuccess", obj);
        } else {
    
    
          loadingInstance.close();
          ElMessage.warning(`文件上传失败`);
        }
      })
      .catch(() => {
    
    
        loadingInstance.close();
        // ElMessage.warning(`文件上传失败`);
      });
  }
  return true;
};

// 校验上传文件格式
const getType = (acceptType: string) => {
    
    
  let val = "";
  switch (acceptType) {
    
    
    case "xls":
      val = "excel";
      break;
    case "doc":
      val = "word";
      break;
    case "pdf":
      val = "pdf";
      break;
    case "zip":
      val = "zip";
      break;
    case "xlsx":
      val = "sheet";
      break;
    case "pptx":
      val = "presentation";
      break;
    case "docx":
      val = "document";
      break;
    case "text":
      val = "text";
      break;
  }
  return val
};

// 移除文件
const removeFile = (file: any) => {
    
    
  const arr: any[] = [...waitFileList.value];
  waitFileList.value = arr.filter((its: any) => {
    
    
    return its.id != file.id;
  });
  emits("fileRemove", waitFileList.value);
};

const handleDownLoad = (row: {
    
     ossFile: string }) => {
    
    
  const str = window.location.href.split("#")[0];
  const herf = str.slice(0, str.length - 1);
  window.location.href = herf + row.ossFile;
};
</script>

<style lang="scss" scoped>
.upload_wrap {
    
    
  .upload {
    
    
    min-width: 468px;
    padding-bottom: 10px;
  }
  .tips {
    
    
    display: block;
    font-size: 14px;
    font-family: PingFangSC-Regular, PingFang SC;
    font-weight: 400;
    color: rgba(0, 0, 0, 0.65);
  }
}

:deep().el-upload__text {
    
    
  width: 106px;
  height: 32px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: #ffffff;
  border: 1px solid rgba(0, 0, 0, 0.15);
  img {
    
    
    display: block;
    width: 14px;
    height: 14px;
  }

  span {
    
    
    font-size: 14px;
    padding-left: 6px;
    font-family: PingFangSC-Regular, PingFang SC;
    font-weight: 400;
    color: rgba(0, 0, 0, 0.65);
  }
}

.template_list {
    
    
  padding-bottom: 4px;
}
.template {
    
    
  display: flex;
  align-items: center;
  padding: 5px 0;
  span {
    
    
    line-height: 16px;
  }
  img {
    
    
    margin-right: 8px;
    width: 16px;
    height: 16px;
  }
  .documentName {
    
    
    margin-right: 12px;
    font-size: 14px;
    color: rgba(0, 0, 0, 0.65);
  }
}
</style>

2、上传图片相关代码及分析

<template>
  <div class="avatar-uploader">
    <el-upload
      class="avatar-uploader"
      :show-file-list="false"
      :disabled="disabledType"
      :auto-upload="false"
      accept=".png,.jepg,.jpg"
      ref="excelUploadRef"
      :on-change="handleChange"
    >
      <img
        v-if="imagesURL || props.imageUrl"
        :src="props.imageUrl ? props.imageUrl : imagesURL"
        class="avatar"
      />
      <div class="upImgBox" v-else>
        <el-icon class="avatar-uploader-icon"><Plus /></el-icon>
        <div>{
    
    {
    
     imgUpText }}</div>
      </div>
    </el-upload>
    <div class="upImgText">
      {
    
    {
    
     imgText }}
    </div>
  </div>
</template>

<script lang="ts" setup>
import {
    
     ref, watch } from "vue";
import {
    
     Plus } from "@element-plus/icons-vue";
import {
    
     ElMessage, ElLoading } from "element-plus";
import {
    
     request } from "@/api/axios";
interface Props {
    
    
  imageUrl?: string; // 回显图片地址
  action?: string; //   上传地址
  imgText?: string; //   文字可以不传
  imgUpText?: string; // 上传按钮的文字
  disabledType?: boolean; // 是否禁用上传
}
const props = withDefaults(defineProps<Props>(), {
    
    
  imageUrl: "",
  action: "/activity/resource/uploadFile",
  imgText: "支持jpg/jpeg/png;文件大小不能超过2M;封面图建议尺寸940px*400px",
  imgUpText: "上传封面",
  disabledType: false,
});

const imagesURL = ref<string>(props.imageUrl);
const emits = defineEmits(["imgSuccess"]);
const handleChange = (file: any, fileList: any) => {
    
    
  console.log(file);
  console.log(fileList);
  let rawFile = file.raw;
  if (
    rawFile.type !== "image/jpeg" &&
    rawFile.type !== "image/png" &&
    rawFile.type !== "image/jpg"
  ) {
    
    
    ElMessage.error("仅支持格式为jpg, jpeg, png的图片");
    return false;
  } else if (rawFile.size / 1024 / 1024 > 2) {
    
    
    ElMessage.error("图片文件大小不能超过2MB!");
    return false;
  } else {
    
    
    let formData = new FormData();
    formData.append("file", rawFile);
    formData.append("fileType", "1");
    const loadingInstance = ElLoading.service({
    
    
      text: "正在上传",
      background: "rgba(0,0,0,.2)",
    });
    // 请求接口上传图片到服务器
    let requestURL = props.action;
    request("post", requestURL, formData, {
    
    
      headers: {
    
     "Content-Type": "multipart/form-data" },
    })
      .then(async (res: any) => {
    
    
        console.log(res);
        if (res.code == 0) {
    
    
          loadingInstance.close();
          let obj = {
    
    
            imgUrl: res.data.ossFile,
            raw: res.data,
          };
          emits("imgSuccess", obj);
          imagesURL.value = res.data.ossFile;
        } else {
    
    
          loadingInstance.close();
          ElMessage.warning(`文件上传失败`);
        }
      })
      .catch(() => {
    
    
        loadingInstance.close();
        ElMessage.warning(`文件上传失败`);
      });
  }
  return true;
};
watch(
  () => props.imageUrl,
  () => {
    
    
    imagesURL.value = props.imageUrl;
  }
);
</script>

<style lang="scss" scoped>
:deep().avatar-uploader {
    
    
  .avatar {
    
    
    width: 104px;
    height: 104px;
    display: block;
  }
  .el-upload {
    
    
    border: 1px dashed #dcdfe6;
    border-radius: 6px;
    cursor: pointer;
    position: relative;
    overflow: hidden;
    transition: 0.2s;
    background: rgba(0, 0, 0, 0.04) !important;
  }

  .el-upload:hover {
    
    
    border-color: #14b194;
  }
}
.el-icon.avatar-uploader-icon {
    
    
  font-size: 16px;
  color: rgba(0, 0, 0, 0.45);
  text-align: center;
}
.upImgBox {
    
    
  width: 104px;
  height: 104px;
  font-size: 14px;
  font-family: PingFangSC-Regular, PingFang SC;
  font-weight: 400;
  color: rgba(0, 0, 0, 0.65);
  text-align: center;
  padding-top: 24px;
  box-sizing: border-box;
}
.upImgText {
    
    
  font-size: 14px;
  color: rgba(0, 0, 0, 0.6);
  margin-top: 4px;
}
</style>

猜你喜欢

转载自blog.csdn.net/LiaoFengJi/article/details/128917787