当前位置: 首页 > news >正文

el-upload 上传逻辑和ui解耦,上传七牛

解耦的作用在于如果后面要我改成从阿里云oss上传文件,我只需要实现上传逻辑从七牛改成阿里云即可,其他不用动。实现方式有2部分组成,一部分是上传逻辑,一部分是ui。

上传逻辑

大概逻辑就是先去服务端拿上传token和地址,然后用http上传

import { upload } from "@/api/agent-go";
import { isShowErr } from "..";
import axios from "axios";
import { UploadProgressEvent } from "element-plus";export async function uploadFile(file: File,classType: uploadClassType,onUploadProgress?: (progressEvent: UploadProgressEvent) => void
) {if (!file) throw new Error("上传文件失败:缺少file参数");if (!classType) throw new Error("上传文件失败:缺少classType参数");const [res, err] = await upload({fileList: [{ classType, name: file.name }],});if (isShowErr(err) || !res.tokenList[0]) throw new Error("获取token失败");const token = res.tokenList[0];const formData = new FormData();formData.append("file", file);formData.append("token", token.token);formData.append("key", token.key);const config = {headers: { "Content-Type": "multipart/form-data" },onUploadProgress: function (progressEvent) {// 处理上传进度const percentCompleted = Math.round((progressEvent.loaded * 100) / progressEvent.total);progressEvent.percent = percentCompleted;if (onUploadProgress) onUploadProgress(progressEvent);},};const response = await axios.post(res.addr, formData, config);return response.data.key; // 返回上传的文件key,如果没有response.data.key,则上传失败
}

UI层

关键就是重写http-request用上我们上面写的上传逻辑

<template><el-uploadref="uploadRef"style="width: 100%":before-upload="beforeUpload":http-request="filehandleFileUpload":on-exceed="handleExceed":on-preview="handlePreview":limit="limit"v-model:file-list="fileList"drag><el-icon class="el-icon--upload"><upload-filled /></el-icon><div class="el-upload__text"><em>点击</em>或将文件<em>拖拽</em>到这里上传<div class="el-upload__tip">{{ text }}</div></div><template #tip> </template><ImageViewer ref="imageViewerRef" /></el-upload>
</template><script setup lang="ts">
import { PropType, ref, watch } from "vue";
import { ElMessage, UploadInstance, genFileId } from "element-plus";
import { uploadFile } from "@/utils/upload";
import {UploadRawFile,UploadRequestOptions,UploadUserFile,
} from "element-plus/es/components";
import { isShowErr } from "@/utils";
import { OssFile, download } from "@/api/agent-go";
import { onMounted } from "vue";
import ImageViewer from "@/components/ImageViewer/index.vue";interface UploadOssFile extends UploadRawFile {ossFileData?: OssFile;
}const props = defineProps({ossFileList: {type: Array as PropType<OssFile[]>,default: () => [],},text: {type: String,default: "请上传excel文件",},whiteList: {type: Array as PropType<string[]>,default: () => ["xls", "xlsx"],},classType: {type: String as PropType<uploadClassType>,default: "temp",},limit: {type: Number,default: 1,},
});const uploadRef = ref<UploadInstance>();
const fileList = ref<UploadUserFile[]>([]);watch(() => props.ossFileList,() => {setValue(props.ossFileList);},{immediate: true,}
);function setValue(val: OssFile[]) {fileList.value = val.map((item) => {const file = new File([], item.name) as UploadOssFile;file.ossFileData = item;file.uid = genFileId();return {name: item.name,status: "success",raw: file,};});
}const filehandleFileUpload = async (options: UploadRequestOptions) => {const key = await uploadFile(options.file, props.classType, (e) => {options.onProgress(e);});const params: OssFile = {name: options.file.name,size: options.file.size,classType: props.classType,mimeType: options.file.type,key,};const file = options.file as UploadOssFile;if (file.ossFileData) file.ossFileData.key = key;//ossFilemap.set(options.file.uid, params);return params;
};const handleExceed = (files) => {if (props.limit > 1) {if (fileList.value.length >= props.limit) {ElMessage({type: "error",message: `最多上传${props.limit}个文件, 请删除后再上传`,});return false;}return;}uploadRef.value!.handleRemove(fileList.value[0] as UploadRawFile);const file = files[0] as UploadRawFile;file.uid = genFileId();uploadRef.value!.handleStart(file);uploadRef.value!.submit();
};const imageViewerRef = ref();
const handlePreview = async (file) => {try {const fileSuffix = file.name.substring(file.name.lastIndexOf("."))?.toLowerCase();const isPictures = [".jpg", ".jpeg", ".png", ".gif"].includes(fileSuffix);const [res, err] = await download({fileList: [file.raw.ossFileData as OssFile],method: isPictures ? "default" : "download",});if (isShowErr(err)) return;const map = res.urlMap;const urls = Object.keys(map)?.map((key) => map[key]?.url);if (!urls || urls.length === 0)return ElMessage.error("下载文件失败, 获取不到文件地址");if (isPictures) {imageViewerRef.value.openViewer(0, urls);} else {// 创建一个隐藏的可下载链接const url = urls[0] || "";const link = document.createElement("a");link.href = url;link.setAttribute("download", file.name); // 设置下载后的文件名document.body.appendChild(link);link.click(); // 模拟点击事件触发下载document.body.removeChild(link); // 清理临时元素}} catch (error) {alert("下载文件失败:" + error);}
};const beforeUpload = (file: UploadOssFile) => {//console.log("beforeUpload", file);file.ossFileData = {name: file.name,size: file.size,classType: props.classType,mimeType: file.type,};const isAnyType = props.whiteList.indexOf("*") != -1;const fileSuffix = file.name.substring(file.name.lastIndexOf(".") + 1);const isSuffix = props.whiteList.indexOf(fileSuffix.toLowerCase()) === -1;const isLt = file.size / 1024 / 1024 > 10;let msg;if (isSuffix && !isAnyType) {msg = "上传文件只能是 " + props.whiteList.join(",") + " 格式";}if (isLt) {msg = "上传文件大小不能超过 10MB";}if (msg) {ElMessage({type: "error",message: msg,});return false;}
};function getValue() {const val: OssFile[] = [];let valid = true;fileList.value.forEach((item) => {if (item.status === "uploading" || item.status === "ready") {ElMessage({type: "error",message: "有文件正在上传中,请稍后再试",});valid = false;return;}if (item.status === "fail") {ElMessage({type: "error",message: "有文件上传失败,请重新上传",});valid = false;return;}const file = item.raw as UploadOssFile;if (file.ossFileData) val.push(file.ossFileData);});if (!valid) {throw new Error("文件上传中或上传失败");}return val;
}defineExpose({getValue,
});
</script>
<style lang="scss" scoped></style>

相关文章:

  • Vue里面elementUi-aside 和el-main不垂直排列
  • ClickHouse 中`MergeTree` 和 `ReplicatedMergeTree`表引擎区别
  • 深入理解机器学习:人工智能的核心驱动力
  • OSPF网络协议
  • 【XR手柄交互】Unity 中使用 InputActions 实现手柄控制详解(基于 OpenXR + Unity新输入系统(Input Actions))
  • Windows环境下常用网络命令使用
  • SIEMENS PLC程序解读 ST 语言 车型识别
  • C++面试复习日记(8)2025.4.25,malloc,free和new,delete的区别
  • HDRnet——双边滤波和仿射变换的摇身一变
  • vite+vue构建的网站项目localhost:5173打不开
  • MYSQL之数据类型
  • 从多类缺陷到高良率跃升|公差分析技术重构动力电池装配精度体系
  • Golang | HashMap实现原理
  • electron-builder 打包安装与启动手动安装,最终解决方案,之前的文章与其他的人都不用看了。
  • 面向对象编程核心:封装、继承、多态与 static 关键字深度解析
  • 使用 uv 工具快速创建 MCP 服务(Trae 配置并调用 MCP 服务)
  • 百度Create2025 AI开发者大会:模型与应用的未来已来
  • 【HTTP/2和HTTP/3的应用现状:看不见的革命】
  • Linux驱动开发快速上手指南:从理论到实战
  • 大内存生产环境tomcat-jvm配置实践
  • 航行警告!黄海南部进行实弹射击,禁止驶入
  • “80后”李岩已任安徽安庆市领导
  • 国家市场监管总局:民生无小事,严打民生领域侵权假冒违法行为
  • 期待会师!神二十与空间站完成对接
  • 城事|喊侬白相,长兴太湖9号公路邀上海市民共赴诗意之旅
  • 降低血压可减少痴呆症发生风险