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

前端vue+elementplus实现上传通用组件

上传组件:

<template><div class="upload-file"><el-uploadmultiple:action="uploadFileUrl":before-upload="handleBeforeUpload":file-list="fileList":limit="limit":on-error="handleUploadError":on-exceed="handleExceed":on-success="handleUploadSuccess":show-file-list="false":headers="headers"class="upload-file-uploader"ref="fileUploadRef"><!-- 上传按钮 --><el-button type="primary">选取文件</el-button></el-upload><!-- 上传提示 --><div class="el-upload__tip" v-if="showTip">请上传<template v-if="fileSize">大小不超过 <b style="color: #f56c6c">{{ fileSize }}MB</b></template><template v-if="fileType">格式为 <b style="color: #f56c6c">{{ fileType.join("/") }}</b></template>的文件</div><!-- 文件列表 --><transition-group class="upload-file-list el-upload-list el-upload-list--text" name="el-fade-in-linear" tag="ul"><li :key="file.uid" class="el-upload-list__item ele-upload-list__item-content" v-for="(file, index) in fileList"><el-link :href="`${file.url}`" :underline="false" target="_blank"><span class="el-icon-document"> {{ getFileName(file.name) }} </span></el-link><div class="ele-upload-list__item-content-action"><el-link :underline="false" @click="handleDelete(index)" type="danger">删除</el-link></div></li></transition-group></div>
</template><script setup lang="ts">
import { listByIds, delOss } from "@/api/system/oss";
import { propTypes } from '@/utils/propTypes';
import { globalHeaders } from "@/utils/request";const props = defineProps({modelValue: [String, Object, Array],// 数量限制limit: propTypes.number.def(5),// 大小限制(MB)fileSize: propTypes.number.def(5),// 文件类型, 例如['png', 'jpg', 'jpeg']fileType: propTypes.array.def(["doc", "xls", "ppt", "txt", "pdf"]),// 是否显示提示isShowTip: propTypes.bool.def(true),
});const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const emit = defineEmits(['update:modelValue']);
const number = ref(0);
const uploadList = ref<any[]>([]);const baseUrl = import.meta.env.VITE_APP_BASE_API;
const uploadFileUrl = ref(baseUrl + "/resource/oss/upload"); // 上传文件服务器地址
const headers = ref(globalHeaders());const fileList = ref<any[]>([]);
const showTip = computed(() => props.isShowTip && (props.fileType || props.fileSize)
);const fileUploadRef = ref<ElUploadInstance>();watch(() => props.modelValue, async val => {if (val) {let temp = 1;// 首先将值转为数组let list = [];if (Array.isArray(val)) {list = val;} else {const res = await listByIds(val as string)list = res.data.map((oss) => {const data = { name: oss.originalName, url: oss.url, ossId: oss.ossId };return data;});}// 然后将数组转为对象数组fileList.value = list.map(item => {item = { name: item.name, url: item.url, ossId: item.ossId };item.uid = item.uid || new Date().getTime() + temp++;return item;});} else {fileList.value = [];return [];}
}, { deep: true, immediate: true });// 上传前校检格式和大小
const handleBeforeUpload = (file: any) => {// 校检文件类型if (props.fileType.length) {const fileName = file.name.split('.');const fileExt = fileName[fileName.length - 1];const isTypeOk = props.fileType.indexOf(fileExt) >= 0;if (!isTypeOk) {proxy?.$modal.msgError(`文件格式不正确, 请上传${props.fileType.join("/")}格式文件!`);return false;}}// 校检文件大小if (props.fileSize) {const isLt = file.size / 1024 / 1024 < props.fileSize;if (!isLt) {proxy?.$modal.msgError(`上传文件大小不能超过 ${props.fileSize} MB!`);return false;}}proxy?.$modal.loading("正在上传文件,请稍候...");number.value++;return true;
}// 文件个数超出
const handleExceed = () => {proxy?.$modal.msgError(`上传文件数量不能超过 ${props.limit} 个!`);
}// 上传失败
const handleUploadError = () => {proxy?.$modal.msgError("上传文件失败");
}// 上传成功回调
const handleUploadSuccess = (res: any, file: UploadFile) => {if (res.code === 200) {uploadList.value.push({ name: res.data.fileName, url: res.data.url, ossId: res.data.ossId });uploadedSuccessfully();} else {number.value--;proxy?.$modal.closeLoading();proxy?.$modal.msgError(res.msg);fileUploadRef.value?.handleRemove(file);uploadedSuccessfully();}
}// 删除文件
const handleDelete = (index: number) => {let ossId = fileList.value[index].ossId;delOss(ossId);fileList.value.splice(index, 1);emit("update:modelValue", listToString(fileList.value));
}// 上传结束处理
const uploadedSuccessfully = () => {if (number.value > 0 && uploadList.value.length === number.value) {fileList.value = fileList.value.filter(f => f.url !== undefined).concat(uploadList.value);uploadList.value = [];number.value = 0;emit("update:modelValue", listToString(fileList.value));proxy?.$modal.closeLoading();}
}// 获取文件名称
const getFileName = (name: string) => {// 如果是url那么取最后的名字 如果不是直接返回if (name.lastIndexOf("/") > -1) {return name.slice(name.lastIndexOf("/") + 1);} else {return name;}
}
// 对象转成指定字符串分隔
const listToString = (list: any[], separator?: string) => {let strs = "";separator = separator || ",";list.forEach(item => {if (item.ossId) {strs += item.ossId + separator;}})return strs != "" ? strs.substring(0, strs.length - 1) : "";
}
</script><style scoped lang="scss">
.upload-file-uploader {margin-bottom: 5px;
}.upload-file-list .el-upload-list__item {border: 1px solid #e4e7ed;line-height: 2;margin-bottom: 10px;position: relative;
}.upload-file-list .ele-upload-list__item-content {display: flex;justify-content: space-between;align-items: center;color: inherit;
}.ele-upload-list__item-content-action .el-link {margin-right: 10px;
}
</style>

自定义组件:

<template><div><el-dialog @close="onClose"  :close-on-click-modal="false" :title="props.title" v-model="props.visible" append-to-body><div>
<!--        <DropFileUpload v-if="form.zhuangtai == '0'" :file-size="100" v-model="form.wenJianId" :file-type="['pdf']" :limit="1"></DropFileUpload>--><file-upload v-model="form.wenJianId" :file-type="['pdf']" :limit="5" :file-size="10" /></div><el-table :data="shenBaoShuList"><el-table-column label="文件名称" align="left" prop="originalName"><template #default="scope"><el-link link type="primary" @click="handleShowPdfViewer(scope.row.ossId)">{{scope.row.originalName}}</el-link></template></el-table-column><el-table-column label="上传时间" width="200" align="center" prop="createTime" /><el-table-column label="操作" width="100" align="center"><template #default="scope"><el-button size="small" :disabled="form.zhuangtai == '1'" type="danger" @click="handleDelete(scope.row)" >删除</el-button></template></el-table-column></el-table></el-dialog><el-drawer v-model="showPdfViewer.visible" :title="showPdfViewer.title" direction="rtl" size="60%"><oss-pdf-viewer v-if="showPdfViewer.visible" :oss-id="showPdfViewer.ossId"></oss-pdf-viewer></el-drawer></div>
</template>
<script setup lang="ts">
// 所有的专科
import DropFileUpload from '@/components/DropFileUpload/index.vue'import ossPdfViewer from '@/components/PdfViewer/oss.vue';
import {defineEmits} from "vue";
import FileUpload from "@/components/FileUpload/index.vue";
import {SysOssVo} from '@/api/medicaltechnology/beianKeshiDetail/types';const {proxy} = getCurrentInstance() as ComponentInternalInstance;
const {shenbaoshu_zhuangtai} = toRefs<any>(proxy?.useDict('shenbaoshu_zhuangtai'));
const emit = defineEmits<{(e: 'update:visible', value: boolean): void;
}>();
const shenBaoShuList = ref<SysOssVo[]>([]);
const form = ref({masterId:'',wenJianId: '',//可以上传多个文件,用,隔开zhuangtai: ''
});interface ShowPdfViewer {title: string;ossId?: string | number;visible: boolean;
}// 文件预览
const showPdfViewer = ref<ShowPdfViewer>({title: '',ossId: '',visible: false,
});
const props = defineProps({title: {type: String,default: ''},sbszhuangtai: {type: String,default: ''},visible: {type: Boolean,default: false},masterId: {type: [ Number],default: ''},type: {type: String,default: ''  //附件清单上传,科室上传,负责人上传 file,dept,leader}
});
const onClose = () => {emit('update:visible', false);};
// 获取文件列表
const getFileList = async () => {console.log(props.masterId);//根据不同类型去获取文件列表shenBaoShuList.value = [];// const res = await getFileListByZhuBiaoId(props.zhuBiaoId as number);// if (res.data) { // 检查 res.data 是否为 null//   shenBaoShuList.value.push(res.data);// }
};/** 删除按钮操作 */
const handleDelete = async (row?: SysOssVo) => {await proxy?.$modal.confirm('是否确认删除选择项?');//根据文件类型去删除// await deleteFileByFileId(row?.ossId || 0);proxy?.$modal.msgSuccess('删除成功');await getFileList();
};
//文件预览
const handleShowPdfViewer = (ossid: number) => {showPdfViewer.value.visible = trueshowPdfViewer.value.ossId = ossid
}
watch(() => form.value.wenJianId, () => {//根据文件类型触发上传// uploadZiPingFile(form.value.zhuBiaoId , form.value.wenJianId).then(() => {//   proxy?.$modal.msgSuccess('上传成功');//   getFileList()// })
})watch(() => props.masterId, () => {// form.value.zhuangtai = props.sbszhuangtaiform.value.masterId = props.masterIdgetFileList()
})
</script>
<style scoped lang="scss"></style>

页面实际调用:、

  <!--  上传附件的对话框--><qing-dan-upload:sbszhuangtai="showOtherFiles.zhuangtai":zhuBiaoId="showOtherFiles.zhuBiaoId"v-model:visible="showOtherFiles.visible":title="showOtherFiles.title"></qing-dan-upload>//上传
const showOtherFiles = reactive({visible: false, //对话跨title: '', //标题masterId: 0,  //主表idtype: ''
});
//上传清单附件
const openUploadFileDialog = (row: QingdanVO,type:string) => {showOtherFiles.visible = true;showOtherFiles.title = row?.cailiaoMingcheng + '附件';showOtherFiles.masterId = row?.masterId;showOtherFiles.type = type;
};

pdf预览组件:oss.vue

<template><!--  {{pdfUrl}}--><iframe :src="pdfUrl" style="border: none; width: 100%; height: 100%;"></iframe><!--    <pdf-viewer :pdf-url="pdfUrl"></pdf-viewer>-->
</template><script setup name="OssPdfViewer" lang="ts">
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const props = defineProps({ossId: {type: [String , Number],required: true,},
});
const pdfUrl = ref('')
const handleDownload = () => {proxy?.$download.ossBlod(props.ossId).then(data=>{const blob = new Blob([data],{type:'application/pdf'})let url = window.URL.createObjectURL(blob)pdfUrl.value = url})
}
watch(()=>props.ossId,()=>{handleDownload()
})onMounted(()=>{handleDownload()
})
</script>

相关文章:

前端vue+elementplus实现上传通用组件

上传组件&#xff1a; <template><div class"upload-file"><el-uploadmultiple:action"uploadFileUrl":before-upload"handleBeforeUpload":file-list"fileList":limit"limit":on-error"handleUploadE…...

联合类型的逻辑或关系与类型保护

在 TypeScript 中&#xff0c;联合类型&#xff08;Union Types&#xff09;是一种强大的类型工具&#xff0c;它允许一个变量可以是几种不同类型中的一种。联合类型通过逻辑“或”关系&#xff08;|&#xff09;连接多个类型。这种类型的灵活性使得我们能够处理多样化的数据输…...

spring boot3.0自定义校验注解:文章状态校验示例

文章目录 Spring Boot 自定义校验注解&#xff1a;状态校验示例一、创建 State 注解步骤&#xff1a;1. 创建自定义注解&#xff1a;2. 实现校验逻辑&#xff1a; 二、 实现自定义校验步骤:1. 在实体类中使用自定义校验注解 State&#xff1a;2. 添加 State 注解&#xff1a; 总…...

神经网络是如何工作的

人工智能最核心的技术之一&#xff0c;就是神经网络&#xff08;Neural Networks&#xff09;。但很多初学者会觉得它是个黑盒&#xff1a;为什么神经网络能识别图片、翻译语言&#xff0c;甚至生成文章&#xff1f; 本文用图解最小代码实现的方式&#xff0c;带你深入理解&am…...

【工作记录】crmeb后端项目打开、运行

1、下载代码 1&#xff09;安装git 不再详述 2&#xff09;git拉代码 项目地址如下&#xff0c;在vscode-分支中拉代码 # 克隆项目 git clone https://gitee.com/ZhongBangKeJi/crmeb_java/ 截图如下是已经成功拉下来 注意安装对应版本 2、maven配置 安装配置见&#x…...

智能手表测试计划文档(软/硬件)

&#x1f4c4; 智能手表测试计划文档&#xff08;软/硬件&#xff09; 项目名称&#xff1a;Aurora Watch S1 文档编号&#xff1a;AW-S1-QA-TP-001 编制日期&#xff1a;2025-xx-xx 版本&#xff1a;V1.0 编写人&#xff1a;xxx&#xff08;测试主管&#xff09; 一、测试目标…...

DeepSeek:开启能源领域智能化变革新时代

目录 一、DeepSeek 与能源领域变革的邂逅1.1 DeepSeek 在人工智能领域的地位与特点1.2 能源行业面临的挑战与变革需求1.3 DeepSeek 在能源领域应用的重要性和意义 二、能源政策解读与科普新助手2.1 能源政策解读的深度变革2.2 能源科普的创新使者 三、能源项目可行性分析新利器…...

红黑树算法笔记(二)性能对比实验

文章目录 1. 实验目标2. 对比数据结构3. 性能指标3.1 时间性能指标3.2 空间性能指标3.3 其他性能指标 4. 测试场景4.1 数据集特性变化4.2 操作模式变化4.3 环境因素变化 5. 实验设计5.1 基准测试设计5.1.1 CRUD性能基准测试5.1.2 混合负载测试5.1.3 范围查询测试 5.2 特殊场景测…...

Nlog适配达梦数据库进行日志插入

前言 原来使用的是SQLServer数据库&#xff0c;使用Nlog很流畅&#xff0c;没有什么问题。现在有个新项目需要使用麒麟操作系统和达梦数据库&#xff0c;业务流程开发完成之后发现Nlog配置文件中把数据库连接内容修改之后不能执行插入操作。 原Nlog.config配置 <?xml ve…...

k8s监控方案实践(三):部署与配置Grafana可视化平台

k8s监控方案实践&#xff08;三&#xff09;&#xff1a;部署与配置Grafana可视化平台 文章目录 k8s监控方案实践&#xff08;三&#xff09;&#xff1a;部署与配置Grafana可视化平台一、Grafana简介1. 什么是Grafana&#xff1f;2. Grafana与Prometheus的关系3. Grafana应用场…...

嵌入式系统架构验证工具:AADL Inspector v1.10 全新升级

软件架构建模与早期验证是嵌入式应用的关键环节。架构分析与设计语言&#xff08;AADL&#xff09;是专为应用软件及执行平台架构模型设计的语言&#xff0c;兼具文本与图形化的双重特性。AADL Inspector是一款轻量级的独立工具&#xff1a; 核心处理能力包括 √ 支持处理AA…...

STM32-模电

目录 一、MOS管 二、二极管 三、IGBT 四、运算放大器 五、推挽、开漏、上拉电阻 一、MOS管 1. MOS简介 这里以nmos管为例&#xff0c;注意箭头方向。G门极/栅极&#xff0c;D漏极&#xff0c;S源极。 当给G通高电平时&#xff0c;灯泡点亮&#xff0c;给G通低电平时&a…...

华为云Flexus+DeepSeek征文|从开通到应用:华为云DeepSeek-V3/R1商用服务深度体验

前言 本文章主要讲述在华为云ModelArts Studio上 开通DeepSeek-V3/R1商用服务的流程&#xff0c;以及开通过程中的经验分享和使用感受帮我更多开发者&#xff0c;在华为云平台快速完成 DeepSeek-V3/R1商用服务的开通以及使用入门注意&#xff1a;避免测试过程中出现部署失败等问…...

鸿蒙NEXT开发动画案例5

1.创建空白项目 2.Page文件夹下面新建Spin.ets文件&#xff0c;代码如下&#xff1a; /*** TODO SpinKit动画组件 - Pulse 脉冲动画* author: CSDN—鸿蒙布道师* since: 2024/05/09*/ ComponentV2 export struct SpinFive {// 参数定义Require Param spinSize: number 48;Re…...

面试篇:Spring MVC

基础概念 什么是Spring MVC&#xff1f; Spring MVC 是 Spring Framework 提供的一个基于 Servlet 的 Web 框架&#xff0c;属于 MVC&#xff08;Model-View-Controller&#xff09;架构的一种实现。它通过 DispatcherServlet 作为前端控制器&#xff0c;对请求进行分发和调度…...

ctfshow——web入门351~356

SSRF没有出网的部分 web入门351 $ch curl_init($url); 作用&#xff1a;初始化一个 cURL 会话&#xff0c;并设置目标 URL。解释&#xff1a; curl_init($url) 创建一个新的 cURL 资源&#xff0c;并将其与 $url 关联。这里的 $url 是用户提供的&#xff0c;因此目标地址完全…...

C++中六个特殊成员函数的关系

C中六个特殊成员函数的关系 C11之后的版本每个类有六个特殊的成员函数&#xff0c;之所以特殊是因为它们可以在各种情况下由编译器自动提供&#xff1b; 默认构造函数、复制构造函数、复制赋值运算符、析构函数、移动构造函数、移动赋值运算符 关系规则&#xff1a; 1、如果…...

【PostgreSQL数据分析实战:从数据清洗到可视化全流程】金融风控分析案例-10.1 风险数据清洗与特征工程

&#x1f449; 点击关注不迷路 &#x1f449; 点击关注不迷路 &#x1f449; 点击关注不迷路 文章大纲 PostgreSQL金融风控分析案例&#xff1a;风险数据清洗与特征工程实战一、案例背景&#xff1a;金融风控数据处理需求二、风险数据清洗实战&#xff08;一&#xff09;缺失值…...

美女热舞混剪视频批量剪辑生产技术实践:智能处理与原创性提升方案解析

一、引言&#xff1a;短视频工业化生产的技术转型 在美女类短视频内容运营中&#xff0c;通过标准化技术流程实现「高质量、规模化」产出成为核心需求。本文结合实战经验&#xff0c;解析如何通过智能素材重组、AI 语音合成、动态元素叠加等技术手段&#xff0c;构建自动化生产…...

破局智算瓶颈:400G光模块如何重构AI时代的网络神经脉络

一、技术演进与市场需求双重驱动 在数字化转型浪潮下&#xff0c;全球互联网流量正以每年30%的复合增长率持续攀升。根据Dell’Oro Group最新报告&#xff0c;2023年400G光模块市场规模已突破15亿美元&#xff0c;预计2026年将占据数据中心光模块市场60%以上份额。这种爆发式增…...

python标准库--collections - 高性能数据结构在算法比赛的应用

目录 一、deque双端队列 1.头部删除元素popleft&#xff08;&#xff09; 2.BFS&#xff08;广度优先搜索&#xff09;优化 3.滑动窗口&#xff08;双指针&#xff09; 4.实现栈或队列 5. 双向遍历与操作 一、deque双端队列 特点&#xff1a;支持两端 O (1) 时间复杂度的…...

神经网络基础-从零开始搭建一个神经网络

一、什么是神经网络 人工神经网络(Articial Neural Network,简写为ANN)也称为神经网络(NN),是一种模仿生物神经网络和功能的计算模型,人脑可以看做是一个生物神经网络,由众多的神经元连接而成,各个神经元传递复杂的电信号,树突接收到输入信号,然后对信号进行处理,通…...

【Go】优化文件下载处理:从多级复制到零拷贝流式处理

在开发音频处理服务过程中&#xff0c;我们面临一个常见需求&#xff1a;从网络下载音频文件并保存到本地。这个看似简单的操作&#xff0c;实际上有很多优化空间。本文将分享一个逐步优化的过程&#xff0c;展示如何从一个基础实现逐步改进到高效的流式下载方案。 初始实现&a…...

Java 显式锁与 Condition 的使用详解

Java 显式锁与 Condition 的使用详解 在多线程编程中&#xff0c;线程间的协作与同步是核心问题。Java 提供了多种机制来实现线程同步&#xff0c;除了传统的 synchronized 关键字外&#xff0c;ReentrantLock 和 Condition 是更灵活且功能强大的替代方案。本文将详细介绍显式…...

android ViewModel liveData无法监听之多线程下activityViewModels不安全

我们一般的&#xff0c;会遇到liveData无法监听到结果&#xff0c;可能存在主要2种可能&#xff1a; liveData没有正确注册&#xff1b;liveData连续多次设置值&#xff0c;中间的值&#xff0c;会被丢弃&#xff0c;但最后一次是能监听到的。 但是我们容易忽略一种case&…...

#Redis黑马点评#(五)Redisson原理详解

目录 一 基于Redis的分布式锁优化 二 Redisson 1 实现步骤 2 Redisson可重入锁机制 3 Redisson可重试机制 4 Redisson超时释放机制 5 RedissonMultiLock解决主从一致性 三 trylock与lock两者有何区别 四 Redis优化秒杀 一 基于Redis的分布式锁优化 二 Redisson Redis…...

23.(vue3.x+vite)引入组件并动态切换(component)

让多个组件使用同一个挂载点,并动态切换,这就是动态组件 效果截图 A组件代码: <template><div><div>{{message }}</</...

VBA会被Python代替吗

VBA不会完全被Python取代、但Python在自动化、数据分析与跨平台开发等方面的优势使其越来越受欢迎、两者将长期并存且各具优势。 Python以其易于学习的语法、强大的开源生态系统和跨平台支持&#xff0c;逐渐成为自动化和数据分析领域的主流工具。然而&#xff0c;VBA依旧在Exc…...

2025 年福建省职业院校技能大赛网络建设与运维赛项Linux赛题解析

​ 准备环境&#xff1a;系统安装及网络配置 [!TIP] 接下来将完全按照国赛评分标准进行&#xff0c;过程中需要掌握基础的Linux命令以及理解Linux系统&#xff0c;建议大家在做题前将Linux基础命令熟练运用 网络建设与运维赛项详细教程请联系主页一、X86架构计算机操作系统安装…...

SEMI E40-0200 STANDARD FOR PROCESSING MANAGEMENT(加工管理标准)-(三)完结

10 消息服务详情 10.1 本章定义实现加工管理概念所需的消息服务。这些消息已在第8.1节中初步介绍。 协议无关性&#xff1a;这些服务独立于所使用的消息协议&#xff0c;可映射至SECS-II&#xff08;SEMI E5&#xff09;或其他类似协议。 10.1.1 消息服务定义内容包括&#…...