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

一、效果图

1、上传文件、视频

2、上传图片

二、代码分析及全部代码

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

1、上传文件相关代码

<template><div class="upload_wrap"><el-uploadv-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"><imgsrc="@/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[]) => {// 防止多次执行changeconst rawFile = file.raw;const list = props.acceptTypeDesc.split("/");let acceptTypeList = list.map((its:string)=>{return getType(its)})// 如果要检索的字符串值没有出现,则该方法返回 -1const 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-uploadclass="avatar-uploader":show-file-list="false":disabled="disabledType":auto-upload="false"accept=".png,.jepg,.jpg"ref="excelUploadRef":on-change="handleChange"><imgv-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>